From 74151e6419a606c25b6ea9ad2eed05679f6e8fd9 Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 22 Sep 2015 20:55:41 +0100 Subject: [PATCH 01/18] Added TLS cert fail callback --- src/event/server_events.c | 15 +++++++++++++++ src/event/server_events.h | 3 +++ src/xmpp/connection.c | 12 +++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index 160d4472..6c575179 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -638,3 +638,18 @@ sv_ev_muc_occupant_online(const char * const room, const char * const nick, cons occupantswin_occupants(room); } } + +int +sv_ev_certfail(const char * const errormsg, const char * const certname, const char * const certfp, + const char * const notbefore, const char * const notafter) +{ + cons_show(""); + cons_show_error("TLS certficiate verification failed: %s", errormsg); + cons_show(" Issuer : %s", certname); + cons_show(" Fingerprint : %s", certfp); + cons_show(" Start : %s", notbefore); + cons_show(" End : %s", notafter); + cons_show(""); + + return 1; +} diff --git a/src/event/server_events.h b/src/event/server_events.h index 3ef8eae4..e9ada1ce 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -86,5 +86,8 @@ void sv_ev_muc_occupant_online(const char * const room, const char * const nick, void sv_ev_roster_update(const char * const barejid, const char * const name, GSList *groups, const char * const subscription, gboolean pending_out); 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/xmpp/connection.c b/src/xmpp/connection.c index 6f9de18d..be60ad21 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -94,13 +94,16 @@ static GTimer *reconnect_timer; static log_level_t _get_log_level(xmpp_log_level_t xmpp_level); static xmpp_log_level_t _get_xmpp_log_level(); + static void _xmpp_file_logger(void * const userdata, const xmpp_log_level_t level, const char * const area, const char * const msg); + static xmpp_log_t * _xmpp_get_file_logger(); static jabber_conn_status_t _jabber_connect(const char * const fulljid, const char * const passwd, const char * const altdomain, int port); + static void _jabber_reconnect(void); static void _connection_handler(xmpp_conn_t * const conn, @@ -357,6 +360,13 @@ _connection_free_session_data(void) presence_clear_sub_requests(); } +static int +_connection_certfail_cb(const char * const certname, const char * const certfp, + char * const notbefore, const char * const notafter, const char * const errormsg) +{ + return sv_ev_certfail(errormsg, certname, certfp, notbefore, notafter); +} + static jabber_conn_status_t _jabber_connect(const char * const fulljid, const char * const passwd, const char * const altdomain, int port) @@ -408,7 +418,7 @@ _jabber_connect(const char * const fulljid, const char * const passwd, } int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, - _connection_handler, jabber_conn.ctx); + _connection_certfail_cb, _connection_handler, jabber_conn.ctx); if (connect_status == 0) jabber_conn.conn_status = JABBER_CONNECTING; From 9414ad268d79ba3d0e75ef5fbe0e379c4462893d Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 22 Sep 2015 21:42:05 +0100 Subject: [PATCH 02/18] Prompt user to allow or deny untrusted TLS certificates --- src/command/command.c | 26 ++++++++++++++++++++++++-- src/command/commands.c | 7 +++++++ src/command/commands.h | 1 + src/event/server_events.c | 23 ++++++++++++++++++++++- src/ui/core.c | 7 +++++++ src/ui/inputwin.c | 16 ++++++++++++++++ src/ui/inputwin.h | 1 + src/ui/ui.h | 1 + tests/unittests/ui/stub_ui.c | 5 +++++ 9 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 27c8e078..949f21c7 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -186,6 +186,21 @@ static struct cmd_t command_defs[] = "/connect me@chatty server chatty.com port 5443") }, + { "/tls", + cmd_tls, parse_args, 0, 0, NULL, + CMD_TAGS( + CMD_TAG_CONNECTION) + CMD_SYN( + "/tls allow", + "/tls deny") + CMD_DESC( + "Handle TLS certificates. ") + CMD_ARGS( + { "allow", "Allow connection using invalid TLS certificate." }, + { "deny", "Allow connection using invalid TLS certificate." }) + CMD_NOEXAMPLES + }, + { "/disconnect", cmd_disconnect, parse_args, 0, 0, NULL, CMD_TAGS( @@ -1674,6 +1689,7 @@ static Autocomplete inpblock_ac; static Autocomplete receipts_ac; static Autocomplete pgp_ac; static Autocomplete pgp_log_ac; +static Autocomplete tls_ac; /* * Initialise command autocompleter and history @@ -2069,6 +2085,10 @@ cmd_init(void) autocomplete_add(pgp_log_ac, "on"); autocomplete_add(pgp_log_ac, "off"); autocomplete_add(pgp_log_ac, "redact"); + + tls_ac = autocomplete_new(); + autocomplete_add(tls_ac, "allow"); + autocomplete_add(tls_ac, "deny"); } void @@ -2133,6 +2153,7 @@ cmd_uninit(void) autocomplete_free(receipts_ac); autocomplete_free(pgp_ac); autocomplete_free(pgp_log_ac); + autocomplete_free(tls_ac); } gboolean @@ -2313,6 +2334,7 @@ cmd_reset_autocomplete(ProfWin *window) autocomplete_reset(receipts_ac); autocomplete_reset(pgp_ac); autocomplete_reset(pgp_log_ac); + autocomplete_reset(tls_ac); if (window->type == WIN_CHAT) { ProfChatWin *chatwin = (ProfChatWin*)window; @@ -2525,8 +2547,8 @@ _cmd_complete_parameters(ProfWin *window, const char * const input) } } - gchar *cmds[] = { "/prefs", "/disco", "/close", "/subject", "/room" }; - Autocomplete completers[] = { prefs_ac, disco_ac, close_ac, subject_ac, room_ac }; + gchar *cmds[] = { "/prefs", "/disco", "/close", "/subject", "/room", "/tls" }; + Autocomplete completers[] = { prefs_ac, disco_ac, close_ac, subject_ac, room_ac, tls_ac }; for (i = 0; i < ARRAY_SIZE(cmds); i++) { result = autocomplete_param_with_ac(input, cmds[i], completers[i], TRUE); diff --git a/src/command/commands.c b/src/command/commands.c index 61c0373b..5ec0dd1f 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -156,6 +156,13 @@ cmd_execute_alias(ProfWin *window, const char * const inp, gboolean *ran) return TRUE; } +gboolean +cmd_tls(ProfWin *window, const char * const command, gchar **args) +{ + cons_bad_cmd_usage(command); + return TRUE; +} + gboolean cmd_connect(ProfWin *window, const char * const command, gchar **args) { diff --git a/src/command/commands.h b/src/command/commands.h index 89f923ff..3c1d52c7 100644 --- a/src/command/commands.h +++ b/src/command/commands.h @@ -82,6 +82,7 @@ gboolean cmd_chlog(ProfWin *window, const char * const command, gchar **args); gboolean cmd_clear(ProfWin *window, const char * const command, gchar **args); gboolean cmd_close(ProfWin *window, const char * const command, gchar **args); gboolean cmd_connect(ProfWin *window, const char * const command, gchar **args); +gboolean cmd_tls(ProfWin *window, const char * const command, gchar **args); gboolean cmd_decline(ProfWin *window, const char * const command, gchar **args); gboolean cmd_disco(ProfWin *window, const char * const command, gchar **args); gboolean cmd_disconnect(ProfWin *window, const char * const command, gchar **args); diff --git a/src/event/server_events.c b/src/event/server_events.c index 6c575179..e8d6a357 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -650,6 +650,27 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c cons_show(" Start : %s", notbefore); cons_show(" End : %s", notafter); cons_show(""); + cons_show("Use '/tls allow' to accept this certificate"); + cons_show("Use '/tls deny' to reject this certificate"); + cons_show(""); + ui_update(); - return 1; + char *cmd = ui_get_line(); + + while ((g_strcmp0(cmd, "/tls allow") != 0) && (g_strcmp0(cmd, "/tls deny") != 0)) { + cons_show("Use '/tls allow' to accept this certificate"); + cons_show("Use '/tls deny' to reject this certificate"); + cons_show(""); + ui_update(); + free(cmd); + cmd = ui_get_line(); + } + + if (g_strcmp0(cmd, "/tls allow") == 0) { + free(cmd); + return 1; + } else { + free(cmd); + return 0; + } } diff --git a/src/ui/core.c b/src/ui/core.c index 5735f462..9f7ffcd7 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -2106,6 +2106,13 @@ ui_ask_password(void) return inp_get_password(); } +char * +ui_get_line(void) +{ + status_bar_update_virtual(); + return inp_get_line(); +} + char * ui_ask_pgp_passphrase(const char *hint, int prev_fail) { diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 9b4eddfb..5ab2c281 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -225,6 +225,21 @@ inp_close(void) rl_callback_handler_remove(); } +char * +inp_get_line(void) +{ + werase(inp_win); + wmove(inp_win, 0, 0); + _inp_win_update_virtual(); + doupdate(); + char *line = NULL; + while (!line) { + line = inp_readline(); + } + status_bar_clear(); + return line; +} + char* inp_get_password(void) { @@ -275,6 +290,7 @@ _inp_write(char *line, int offset) _inp_win_handle_scroll(); _inp_win_update_virtual(); + doupdate(); } static int diff --git a/src/ui/inputwin.h b/src/ui/inputwin.h index f49a6a76..4f732e67 100644 --- a/src/ui/inputwin.h +++ b/src/ui/inputwin.h @@ -47,5 +47,6 @@ void inp_win_clear(void); void inp_win_resize(void); void inp_put_back(void); char* inp_get_password(void); +char * inp_get_line(void); #endif diff --git a/src/ui/ui.h b/src/ui/ui.h index 27395048..07cccd20 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -102,6 +102,7 @@ win_type_t ui_win_type(int index); void ui_close_win(int index); int ui_win_unread(int index); char * ui_ask_password(void); +char * ui_get_line(void); char * ui_ask_pgp_passphrase(const char *hint, int prev_fail); void ui_handle_stanza(const char * const msg); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 8c5f9701..032564fd 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -168,6 +168,11 @@ char * ui_ask_password(void) return mock_ptr_type(char *); } +char *ui_get_line(void) +{ + return NULL; +} + void ui_handle_stanza(const char * const msg) {} // ui events From 9a53854a1d196e79a031c28b7a6013ac41c58c4c Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 22 Sep 2015 21:55:46 +0100 Subject: [PATCH 03/18] Added conditionals for libmesode cert verification --- src/xmpp/connection.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index be60ad21..c6d04079 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -360,12 +360,14 @@ _connection_free_session_data(void) presence_clear_sub_requests(); } +#ifdef HAVE_LIBMESODE static int _connection_certfail_cb(const char * const certname, const char * const certfp, char * const notbefore, const char * const notafter, const char * const errormsg) { return sv_ev_certfail(errormsg, certname, certfp, notbefore, notafter); } +#endif static jabber_conn_status_t _jabber_connect(const char * const fulljid, const char * const passwd, @@ -417,8 +419,13 @@ _jabber_connect(const char * const fulljid, const char * const passwd, xmpp_conn_disable_tls(jabber_conn.conn); } +#ifdef HAVE_LIBMESODE int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, _connection_certfail_cb, _connection_handler, jabber_conn.ctx); +#else + int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, + _connection_handler, jabber_conn.ctx); +#endif if (connect_status == 0) jabber_conn.conn_status = JABBER_CONNECTING; From b88885aaae349503aebd944f8d91851143c7aee4 Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 22 Sep 2015 22:44:18 +0100 Subject: [PATCH 04/18] Store trusted TLS cert fingerprints --- src/command/command.c | 7 ++-- src/config/preferences.c | 74 +++++++++++++++++++++++++++++++++++++++ src/config/preferences.h | 4 +++ src/event/server_events.c | 17 ++++++++- 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 949f21c7..c89bcb05 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -192,12 +192,14 @@ static struct cmd_t command_defs[] = CMD_TAG_CONNECTION) CMD_SYN( "/tls allow", + "/tls always", "/tls deny") CMD_DESC( "Handle TLS certificates. ") CMD_ARGS( - { "allow", "Allow connection using invalid TLS certificate." }, - { "deny", "Allow connection using invalid TLS certificate." }) + { "allow", "Allow connection to continue with an invalid TLS certificate." }, + { "always", "Always allow connections with this invalid TLS certificate." }, + { "deny", "Terminate TLS connection." }) CMD_NOEXAMPLES }, @@ -2088,6 +2090,7 @@ cmd_init(void) tls_ac = autocomplete_new(); autocomplete_add(tls_ac, "allow"); + autocomplete_add(tls_ac, "always"); autocomplete_add(tls_ac, "deny"); } diff --git a/src/config/preferences.c b/src/config/preferences.c index 6d63d3e9..e11b4cf2 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -430,6 +430,80 @@ 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 2a7ab5bf..432137b3 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -162,4 +162,8 @@ 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/event/server_events.c b/src/event/server_events.c index e8d6a357..d2aaf28b 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -643,6 +643,13 @@ 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); + return 1; + } + prefs_free_trusted_certs(trusted); + cons_show(""); cons_show_error("TLS certficiate verification failed: %s", errormsg); cons_show(" Issuer : %s", certname); @@ -651,14 +658,18 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c cons_show(" End : %s", notafter); cons_show(""); cons_show("Use '/tls allow' to accept this certificate"); + cons_show("Use '/tls always' to accept this certificate permanently"); cons_show("Use '/tls deny' to reject this certificate"); cons_show(""); ui_update(); char *cmd = ui_get_line(); - while ((g_strcmp0(cmd, "/tls allow") != 0) && (g_strcmp0(cmd, "/tls deny") != 0)) { + while ((g_strcmp0(cmd, "/tls allow") != 0) + && (g_strcmp0(cmd, "/tls always") != 0) + && (g_strcmp0(cmd, "/tls deny") != 0)) { cons_show("Use '/tls allow' to accept this certificate"); + cons_show("Use '/tls always' to accept this certificate permanently"); cons_show("Use '/tls deny' to reject this certificate"); cons_show(""); ui_update(); @@ -669,6 +680,10 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c if (g_strcmp0(cmd, "/tls allow") == 0) { free(cmd); return 1; + } else if (g_strcmp0(cmd, "/tls always") == 0) { + prefs_add_trusted_cert(certfp); + free(cmd); + return 1; } else { free(cmd); return 0; From 3d44541069121915d6150b0d9073078a54715b9c Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 22 Sep 2015 22:54:40 +0100 Subject: [PATCH 05/18] Fix travis build --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ade6084b..962a15a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ install: - sudo apt-get -y install libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr2-dev libgpgme11-dev uuid-dev expect-dev tcl-dev - git clone git://github.com/boothj5/libmesode.git - cd libmesode + - git checkout tls-verify - mkdir m4 - ./bootstrap.sh - ./configure --prefix=/usr From a964050b87988bf7238125de509a0bf9c1951bcc Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 22 Sep 2015 23:56:22 +0100 Subject: [PATCH 06/18] Use subject name in cert callback --- src/event/server_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index d2aaf28b..31b8a698 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -652,7 +652,7 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c cons_show(""); cons_show_error("TLS certficiate verification failed: %s", errormsg); - cons_show(" Issuer : %s", certname); + cons_show(" Subject : %s", certname); cons_show(" Fingerprint : %s", certfp); cons_show(" Start : %s", notbefore); cons_show(" End : %s", notafter); From bd9c28c100b09d51920cfd34202a5d0486c2b02c Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 23 Sep 2015 00:17:10 +0100 Subject: [PATCH 07/18] Tidied output for cert callback --- src/event/server_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index 31b8a698..3820f3f4 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -652,7 +652,7 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c cons_show(""); cons_show_error("TLS certficiate verification failed: %s", errormsg); - cons_show(" Subject : %s", certname); + cons_show(" Subject : %s", certname); cons_show(" Fingerprint : %s", certfp); cons_show(" Start : %s", notbefore); cons_show(" End : %s", notafter); From a37d55e1a9664c4ad04b5c19401398e90090da4c Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 23 Sep 2015 20:37:41 +0100 Subject: [PATCH 08/18] Added TLS trusted certificate path preference --- src/command/command.c | 48 ++++++++++++++++++++++++++++++++++------ src/command/commands.c | 32 +++++++++++++++++++++++++-- src/config/preferences.c | 3 +++ src/config/preferences.h | 3 ++- src/xmpp/connection.c | 5 +++++ 5 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index c89bcb05..669fb066 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -106,6 +106,7 @@ static char * _time_autocomplete(ProfWin *window, const char * const input); static char * _receipts_autocomplete(ProfWin *window, const char * const input); static char * _help_autocomplete(ProfWin *window, const char * const input); static char * _wins_autocomplete(ProfWin *window, const char * const input); +static char * _tls_autocomplete(ProfWin *window, const char * const input); GHashTable *commands = NULL; @@ -187,19 +188,25 @@ static struct cmd_t command_defs[] = }, { "/tls", - cmd_tls, parse_args, 0, 0, NULL, + cmd_tls, parse_args, 1, 3, NULL, CMD_TAGS( CMD_TAG_CONNECTION) CMD_SYN( "/tls allow", "/tls always", - "/tls deny") + "/tls deny", + "/tls certpath", + "/tls certpath set ", + "/tls certpath clear") CMD_DESC( "Handle TLS certificates. ") CMD_ARGS( - { "allow", "Allow connection to continue with an invalid TLS certificate." }, - { "always", "Always allow connections with this invalid TLS certificate." }, - { "deny", "Terminate TLS connection." }) + { "allow", "Allow connection to continue with an invalid TLS certificate." }, + { "always", "Always allow connections with this invalid TLS certificate." }, + { "deny", "Terminate TLS connection." }, + { "certpath", "Show the trusted certificate path." }, + { "certpath set ", "Specify filesystem path containing trusted certificates." }, + { "certpath clear", "Clear the trusted certificate path." }) CMD_NOEXAMPLES }, @@ -1692,6 +1699,7 @@ static Autocomplete receipts_ac; static Autocomplete pgp_ac; static Autocomplete pgp_log_ac; static Autocomplete tls_ac; +static Autocomplete tls_certpath_ac; /* * Initialise command autocompleter and history @@ -2092,6 +2100,11 @@ cmd_init(void) autocomplete_add(tls_ac, "allow"); autocomplete_add(tls_ac, "always"); autocomplete_add(tls_ac, "deny"); + autocomplete_add(tls_ac, "certpath"); + + tls_certpath_ac = autocomplete_new(); + autocomplete_add(tls_certpath_ac, "set"); + autocomplete_add(tls_certpath_ac, "clear"); } void @@ -2157,6 +2170,7 @@ cmd_uninit(void) autocomplete_free(pgp_ac); autocomplete_free(pgp_log_ac); autocomplete_free(tls_ac); + autocomplete_free(tls_certpath_ac); } gboolean @@ -2338,6 +2352,7 @@ cmd_reset_autocomplete(ProfWin *window) autocomplete_reset(pgp_ac); autocomplete_reset(pgp_log_ac); autocomplete_reset(tls_ac); + autocomplete_reset(tls_certpath_ac); if (window->type == WIN_CHAT) { ProfChatWin *chatwin = (ProfChatWin*)window; @@ -2550,8 +2565,8 @@ _cmd_complete_parameters(ProfWin *window, const char * const input) } } - gchar *cmds[] = { "/prefs", "/disco", "/close", "/subject", "/room", "/tls" }; - Autocomplete completers[] = { prefs_ac, disco_ac, close_ac, subject_ac, room_ac, tls_ac }; + gchar *cmds[] = { "/prefs", "/disco", "/close", "/subject", "/room" }; + Autocomplete completers[] = { prefs_ac, disco_ac, close_ac, subject_ac, room_ac }; for (i = 0; i < ARRAY_SIZE(cmds); i++) { result = autocomplete_param_with_ac(input, cmds[i], completers[i], TRUE); @@ -2591,6 +2606,7 @@ _cmd_complete_parameters(ProfWin *window, const char * const input) g_hash_table_insert(ac_funcs, "/time", _time_autocomplete); g_hash_table_insert(ac_funcs, "/receipts", _receipts_autocomplete); g_hash_table_insert(ac_funcs, "/wins", _wins_autocomplete); + g_hash_table_insert(ac_funcs, "/tls", _tls_autocomplete); int len = strlen(input); char parsed[len+1]; @@ -3497,6 +3513,24 @@ _wins_autocomplete(ProfWin *window, const char * const input) return NULL; } +static char * +_tls_autocomplete(ProfWin *window, const char * const input) +{ + char *result = NULL; + + result = autocomplete_param_with_ac(input, "/tls certpath", tls_certpath_ac, TRUE); + if (result) { + return result; + } + + result = autocomplete_param_with_ac(input, "/tls", tls_ac, TRUE); + if (result) { + return result; + } + + return result; +} + static char * _receipts_autocomplete(ProfWin *window, const char * const input) { diff --git a/src/command/commands.c b/src/command/commands.c index 5ec0dd1f..f0320ba0 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -159,8 +159,36 @@ cmd_execute_alias(ProfWin *window, const char * const inp, gboolean *ran) gboolean cmd_tls(ProfWin *window, const char * const command, gchar **args) { - cons_bad_cmd_usage(command); - return TRUE; + if (g_strcmp0(args[0], "certpath") == 0) { + if (g_strcmp0(args[1], "set") == 0) { + if (args[2] == NULL) { + cons_bad_cmd_usage(command); + return TRUE; + } + prefs_set_string(PREF_CERT_PATH, args[2]); + cons_show("Certificate path set to: %s", args[2]); + return TRUE; + } else if (g_strcmp0(args[1], "clear") == 0) { + prefs_set_string(PREF_CERT_PATH, NULL); + cons_show("Certificate path cleared"); + return TRUE; + } else if (args[1] == NULL) { + char *path = prefs_get_string(PREF_CERT_PATH); + if (path) { + cons_show("Trusted certificate path: %s", path); + prefs_free_string(path); + } else { + cons_show("No trusted certificate path set."); + } + return TRUE; + } else { + cons_bad_cmd_usage(command); + return TRUE; + } + } else { + cons_bad_cmd_usage(command); + return TRUE; + } } gboolean diff --git a/src/config/preferences.c b/src/config/preferences.c index e11b4cf2..3584b457 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -686,6 +686,7 @@ _get_group(preference_t pref) case PREF_CARBONS: case PREF_RECEIPTS_SEND: case PREF_RECEIPTS_REQUEST: + case PREF_CERT_PATH: return PREF_GROUP_CONNECTION; case PREF_OTR_LOG: case PREF_OTR_POLICY: @@ -818,6 +819,8 @@ _get_key(preference_t pref) return "enc.warn"; case PREF_PGP_LOG: return "log"; + case PREF_CERT_PATH: + return "certpath"; default: return NULL; } diff --git a/src/config/preferences.h b/src/config/preferences.h index 432137b3..9718cfcb 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -103,7 +103,8 @@ typedef enum { PREF_RESOURCE_MESSAGE, PREF_INPBLOCK_DYNAMIC, PREF_ENC_WARN, - PREF_PGP_LOG + PREF_PGP_LOG, + PREF_CERT_PATH, } preference_t; typedef struct prof_alias_t { diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index c6d04079..e4a5bb32 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -419,6 +419,11 @@ _jabber_connect(const char * const fulljid, const char * const passwd, xmpp_conn_disable_tls(jabber_conn.conn); } + char *cert_path = prefs_get_string(PREF_CERT_PATH); + if (cert_path) { + xmpp_conn_tlscert_path(jabber_conn.conn, cert_path); + } + #ifdef HAVE_LIBMESODE int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, _connection_certfail_cb, _connection_handler, jabber_conn.ctx); From 0321adde3ed7f7e6e9b645be2d62a57931e9ba52 Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 23 Sep 2015 20:38:52 +0100 Subject: [PATCH 09/18] Fixed spelling --- src/event/server_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index 3820f3f4..db883dd2 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -651,7 +651,7 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c prefs_free_trusted_certs(trusted); cons_show(""); - cons_show_error("TLS certficiate verification failed: %s", errormsg); + cons_show_error("TLS certificate verification failed: %s", errormsg); cons_show(" Subject : %s", certname); cons_show(" Fingerprint : %s", certfp); cons_show(" Start : %s", notbefore); From 1eab57bd1397e0c88d1b6278c25a5fa44ee26a0b Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 23 Sep 2015 20:42:47 +0100 Subject: [PATCH 10/18] Perform directory exists check before setting TLS cert path --- src/command/commands.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/command/commands.c b/src/command/commands.c index f0320ba0..8c2b70ec 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -165,8 +165,13 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args) cons_bad_cmd_usage(command); return TRUE; } - prefs_set_string(PREF_CERT_PATH, args[2]); - cons_show("Certificate path set to: %s", args[2]); + + if (g_file_test(args[2], G_FILE_TEST_IS_DIR)) { + prefs_set_string(PREF_CERT_PATH, args[2]); + cons_show("Certificate path set to: %s", args[2]); + } else { + cons_show("Directory %s does not exist.", args[2]); + } return TRUE; } else if (g_strcmp0(args[1], "clear") == 0) { prefs_set_string(PREF_CERT_PATH, NULL); From 40ce5cb0e08c892dfa12488488e554e6c6a35f54 Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 23 Sep 2015 23:01:59 +0100 Subject: [PATCH 11/18] Parse TLS cert subject --- src/event/server_events.c | 41 +++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index db883dd2..176973ff 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -650,12 +650,45 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c } prefs_free_trusted_certs(trusted); + char *domain = NULL; + char *org = NULL; + char *email = NULL; + gchar** fields = g_strsplit(certname, "/", 0); + int i = 0; + for (i = 0; i < g_strv_length(fields); i++) { + gchar** keyval = g_strsplit(fields[i], "=", 2); + if (g_strv_length(keyval) == 2) { + if (g_strcmp0(keyval[0], "CN") == 0) { + domain = strdup(keyval[1]); + } + if (g_strcmp0(keyval[0], "O") == 0) { + org = strdup(keyval[1]); + } + if (g_strcmp0(keyval[0], "emailAddress") == 0) { + email = strdup(keyval[1]); + } + } + g_strfreev(keyval); + } + g_strfreev(fields); + cons_show(""); cons_show_error("TLS certificate verification failed: %s", errormsg); - cons_show(" Subject : %s", certname); - cons_show(" Fingerprint : %s", certfp); - cons_show(" Start : %s", notbefore); - cons_show(" End : %s", notafter); + 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); + cons_show(" End : %s", notafter); cons_show(""); cons_show("Use '/tls allow' to accept this certificate"); cons_show("Use '/tls always' to accept this certificate permanently"); From d96e68ea53c6457dbbd441d6dbe13c5c994d7a22 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 00:18:18 +0100 Subject: [PATCH 12/18] 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(); From 6f8ad6b8e80da98273870ea1b241065418a26367 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 00:43:41 +0100 Subject: [PATCH 13/18] Added /tls trusted command --- src/command/command.c | 3 +++ src/command/commands.c | 34 ++++++++++++++++++++++++++++++++++ src/config/tlscerts.c | 28 ++++++++++++++++++++++++++++ src/config/tlscerts.h | 2 ++ 4 files changed, 67 insertions(+) diff --git a/src/command/command.c b/src/command/command.c index 669fb066..bb440bbf 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -195,6 +195,7 @@ static struct cmd_t command_defs[] = "/tls allow", "/tls always", "/tls deny", + "/tls trusted", "/tls certpath", "/tls certpath set ", "/tls certpath clear") @@ -204,6 +205,7 @@ static struct cmd_t command_defs[] = { "allow", "Allow connection to continue with an invalid TLS certificate." }, { "always", "Always allow connections with this invalid TLS certificate." }, { "deny", "Terminate TLS connection." }, + { "trusted", "List manually trusted certificates." }, { "certpath", "Show the trusted certificate path." }, { "certpath set ", "Specify filesystem path containing trusted certificates." }, { "certpath clear", "Clear the trusted certificate path." }) @@ -2100,6 +2102,7 @@ cmd_init(void) autocomplete_add(tls_ac, "allow"); autocomplete_add(tls_ac, "always"); autocomplete_add(tls_ac, "deny"); + autocomplete_add(tls_ac, "trusted"); autocomplete_add(tls_ac, "certpath"); tls_certpath_ac = autocomplete_new(); diff --git a/src/command/commands.c b/src/command/commands.c index 8c2b70ec..70f41a6d 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -49,6 +49,7 @@ #include "config/account.h" #include "config/preferences.h" #include "config/theme.h" +#include "config/tlscerts.h" #include "contact.h" #include "roster_list.h" #include "jid.h" @@ -190,6 +191,39 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args) cons_bad_cmd_usage(command); return TRUE; } + } else if (g_strcmp0(args[0], "trusted") == 0) { + GList *certs = tlscerts_list(); + GList *curr = certs; + + if (curr) { + cons_show("Trusted certificates:"); + cons_show(""); + } + while (curr) { + TLSCertificate *cert = curr->data; + if (cert->domain) { + cons_show("Domain : %s", cert->domain); + } + if (cert->organisation) { + cons_show("Organisation : %s", cert->organisation); + } + if (cert->email) { + cons_show("Email : %s", cert->email); + } + if (cert->notbefore) { + cons_show("Start : %s", cert->notbefore); + } + if (cert->notafter) { + cons_show("End : %s", cert->notafter); + } + if (cert->fingerprint) { + cons_show("Fingerprint : %s", cert->fingerprint); + } + cons_show(""); + curr = g_list_next(curr); + } + g_list_free_full(certs, (GDestroyNotify)tlscerts_free); + return TRUE; } else { cons_bad_cmd_usage(command); return TRUE; diff --git a/src/config/tlscerts.c b/src/config/tlscerts.c index 562e3b0b..7d2220cf 100644 --- a/src/config/tlscerts.c +++ b/src/config/tlscerts.c @@ -68,6 +68,34 @@ tlscerts_exists(const char * const fingerprint) return g_key_file_has_group(tlscerts, fingerprint); } +GList* +tlscerts_list(void) +{ + GList *res = NULL; + gsize len = 0; + gchar **groups = g_key_file_get_groups(tlscerts, &len); + + int i = 0; + for (i = 0; i < g_strv_length(groups); i++) { + char *fingerprint = strdup(groups[i]); + char *domain = g_key_file_get_string(tlscerts, fingerprint, "domain", NULL); + char *organisation = g_key_file_get_string(tlscerts, fingerprint, "organisation", NULL); + char *email = g_key_file_get_string(tlscerts, fingerprint, "email", NULL); + char *notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL); + char *notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL); + + TLSCertificate *cert = tlscerts_new(fingerprint, domain, organisation, email, notbefore, notafter); + + res = g_list_append(res, cert); + } + + if (groups) { + g_strfreev(groups); + } + + return res; +} + 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) diff --git a/src/config/tlscerts.h b/src/config/tlscerts.h index 782d4430..56e81dd6 100644 --- a/src/config/tlscerts.h +++ b/src/config/tlscerts.h @@ -56,6 +56,8 @@ void tlscerts_add(TLSCertificate *cert); void tlscerts_free(TLSCertificate *cert); +GList* tlscerts_list(void); + void tlscerts_close(void); #endif From 8d2c7f1ac0b5e772310b1bb7f324a91bbf8b3805 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 00:44:48 +0100 Subject: [PATCH 14/18] Show trusted certificate fingerprint first --- src/command/commands.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/command/commands.c b/src/command/commands.c index 70f41a6d..94734563 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -201,6 +201,9 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args) } while (curr) { TLSCertificate *cert = curr->data; + if (cert->fingerprint) { + cons_show("Fingerprint : %s", cert->fingerprint); + } if (cert->domain) { cons_show("Domain : %s", cert->domain); } @@ -216,9 +219,6 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args) if (cert->notafter) { cons_show("End : %s", cert->notafter); } - if (cert->fingerprint) { - cons_show("Fingerprint : %s", cert->fingerprint); - } cons_show(""); curr = g_list_next(curr); } From bd421853897b432a2877da549eb7dbc4a67503e3 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 01:06:53 +0100 Subject: [PATCH 15/18] Added /tls revoke --- src/command/command.c | 12 +++++++++++- src/command/commands.c | 14 ++++++++++++++ src/config/tlscerts.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/config/tlscerts.h | 6 ++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/command/command.c b/src/command/command.c index bb440bbf..f2883efc 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -50,6 +50,7 @@ #include "config/accounts.h" #include "config/preferences.h" #include "config/theme.h" +#include "config/tlscerts.h" #include "contact.h" #include "roster_list.h" #include "jid.h" @@ -196,6 +197,7 @@ static struct cmd_t command_defs[] = "/tls always", "/tls deny", "/tls trusted", + "/tls revoke ", "/tls certpath", "/tls certpath set ", "/tls certpath clear") @@ -205,7 +207,8 @@ static struct cmd_t command_defs[] = { "allow", "Allow connection to continue with an invalid TLS certificate." }, { "always", "Always allow connections with this invalid TLS certificate." }, { "deny", "Terminate TLS connection." }, - { "trusted", "List manually trusted certificates." }, + { "trusted", "List manually trusted certificates (with /tls always)." }, + { "revoke", "Remove a manually trusted certificate." }, { "certpath", "Show the trusted certificate path." }, { "certpath set ", "Specify filesystem path containing trusted certificates." }, { "certpath clear", "Clear the trusted certificate path." }) @@ -2103,6 +2106,7 @@ cmd_init(void) autocomplete_add(tls_ac, "always"); autocomplete_add(tls_ac, "deny"); autocomplete_add(tls_ac, "trusted"); + autocomplete_add(tls_ac, "revoke"); autocomplete_add(tls_ac, "certpath"); tls_certpath_ac = autocomplete_new(); @@ -2286,6 +2290,7 @@ cmd_reset_autocomplete(ProfWin *window) muc_invites_reset_ac(); accounts_reset_all_search(); accounts_reset_enabled_search(); + tlscerts_reset_ac(); prefs_reset_boolean_choice(); presence_reset_sub_request_search(); #ifdef HAVE_LIBGPGME @@ -3521,6 +3526,11 @@ _tls_autocomplete(ProfWin *window, const char * const input) { char *result = NULL; + result = autocomplete_param_with_func(input, "/tls revoke", tlscerts_complete); + if (result) { + return result; + } + result = autocomplete_param_with_ac(input, "/tls certpath", tls_certpath_ac, TRUE); if (result) { return result; diff --git a/src/command/commands.c b/src/command/commands.c index 94734563..3f695cc1 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -198,6 +198,8 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args) if (curr) { cons_show("Trusted certificates:"); cons_show(""); + } else { + cons_show("No trustes certificates found."); } while (curr) { TLSCertificate *cert = curr->data; @@ -224,6 +226,18 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args) } g_list_free_full(certs, (GDestroyNotify)tlscerts_free); return TRUE; + } else if (g_strcmp0(args[0], "revoke") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else { + gboolean res = tlscerts_revoke(args[1]); + if (res) { + cons_show("Trusted certificate revoked: %s", args[1]); + } else { + cons_show("Could not find certificate: %s", args[0]); + } + } + return TRUE; } else { cons_bad_cmd_usage(command); return TRUE; diff --git a/src/config/tlscerts.c b/src/config/tlscerts.c index 7d2220cf..3d0a0b24 100644 --- a/src/config/tlscerts.c +++ b/src/config/tlscerts.c @@ -41,6 +41,7 @@ #include "config/tlscerts.h" #include "log.h" #include "common.h" +#include "tools/autocomplete.h" static gchar *tlscerts_loc; static GKeyFile *tlscerts; @@ -48,6 +49,8 @@ static GKeyFile *tlscerts; static gchar* _get_tlscerts_file(void); static void _save_tlscerts(void); +static Autocomplete certs_ac; + void tlscerts_init(void) { @@ -60,6 +63,16 @@ tlscerts_init(void) tlscerts = g_key_file_new(); g_key_file_load_from_file(tlscerts, tlscerts_loc, G_KEY_FILE_KEEP_COMMENTS, NULL); + + certs_ac = autocomplete_new(); + gsize len = 0; + gchar **groups = g_key_file_get_groups(tlscerts, &len); + + int i = 0; + for (i = 0; i < g_strv_length(groups); i++) { + autocomplete_add(certs_ac, groups[i]); + } + g_strfreev(groups); } gboolean @@ -146,6 +159,8 @@ tlscerts_add(TLSCertificate *cert) return; } + autocomplete_add(certs_ac, cert->fingerprint); + if (cert->domain) { g_key_file_set_string(tlscerts, cert->fingerprint, "domain", cert->domain); } @@ -165,6 +180,31 @@ tlscerts_add(TLSCertificate *cert) _save_tlscerts(); } +gboolean +tlscerts_revoke(const char * const fingerprint) +{ + gboolean result = g_key_file_remove_group(tlscerts, fingerprint, NULL); + if (result) { + autocomplete_remove(certs_ac, fingerprint); + } + + _save_tlscerts(); + + return result; +} + +char * +tlscerts_complete(const char * const prefix) +{ + return autocomplete_complete(certs_ac, prefix, TRUE); +} + +void +tlscerts_reset_ac(void) +{ + autocomplete_reset(certs_ac); +} + void tlscerts_free(TLSCertificate *cert) { @@ -183,6 +223,7 @@ tlscerts_close(void) { g_key_file_free(tlscerts); tlscerts = NULL; + autocomplete_free(certs_ac); } static gchar * diff --git a/src/config/tlscerts.h b/src/config/tlscerts.h index 56e81dd6..b8a15c5b 100644 --- a/src/config/tlscerts.h +++ b/src/config/tlscerts.h @@ -54,10 +54,16 @@ gboolean tlscerts_exists(const char * const fingerprint); void tlscerts_add(TLSCertificate *cert); +gboolean tlscerts_revoke(const char * const fingerprint); + void tlscerts_free(TLSCertificate *cert); GList* tlscerts_list(void); +char* tlscerts_complete(const char * const prefix); + +void tlscerts_reset_ac(void); + void tlscerts_close(void); #endif From 3e38d64397d7c26a782410f82f486c3de087bf40 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 01:13:45 +0100 Subject: [PATCH 16/18] Added HAVE_LIBMESODE check before setting cert path --- src/xmpp/connection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index e4a5bb32..768a4476 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -419,10 +419,12 @@ _jabber_connect(const char * const fulljid, const char * const passwd, xmpp_conn_disable_tls(jabber_conn.conn); } +#ifdef HAVE_LIBMESODE char *cert_path = prefs_get_string(PREF_CERT_PATH); if (cert_path) { xmpp_conn_tlscert_path(jabber_conn.conn, cert_path); } +#endif #ifdef HAVE_LIBMESODE int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, From b96e3f100be18931aa534a819bc88ef6ffffc82b Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 01:23:48 +0100 Subject: [PATCH 17/18] Fixed /tls help --- src/command/command.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index f2883efc..891cf4ea 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -204,14 +204,14 @@ static struct cmd_t command_defs[] = CMD_DESC( "Handle TLS certificates. ") CMD_ARGS( - { "allow", "Allow connection to continue with an invalid TLS certificate." }, - { "always", "Always allow connections with this invalid TLS certificate." }, - { "deny", "Terminate TLS connection." }, - { "trusted", "List manually trusted certificates (with /tls always)." }, - { "revoke", "Remove a manually trusted certificate." }, - { "certpath", "Show the trusted certificate path." }, - { "certpath set ", "Specify filesystem path containing trusted certificates." }, - { "certpath clear", "Clear the trusted certificate path." }) + { "allow", "Allow connection to continue with an invalid TLS certificate." }, + { "always", "Always allow connections with this invalid TLS certificate." }, + { "deny", "Terminate TLS connection." }, + { "trusted", "List manually trusted certificates (with /tls always)." }, + { "revoke ", "Remove a manually trusted certificate." }, + { "certpath", "Show the trusted certificate path." }, + { "certpath set ", "Specify filesystem path containing trusted certificates." }, + { "certpath clear", "Clear the trusted certificate path." }) CMD_NOEXAMPLES }, From 5c2e802bd74beb40f72f2352fa702e26ebaaac0b Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 24 Sep 2015 01:24:19 +0100 Subject: [PATCH 18/18] Fixed travis build --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 962a15a3..ade6084b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ install: - sudo apt-get -y install libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr2-dev libgpgme11-dev uuid-dev expect-dev tcl-dev - git clone git://github.com/boothj5/libmesode.git - cd libmesode - - git checkout tls-verify - mkdir m4 - ./bootstrap.sh - ./configure --prefix=/usr