--- a/ChangeLog Tue Nov 04 22:18:30 2014 -0800
+++ b/ChangeLog Wed Nov 05 13:22:39 2014 -0500
@@ -76,14 +76,17 @@
was an offline message. (Flavius Anton) (#2497)
+ * Fix handling of Self-Signed SSL/TLS Certificates when using the NSS + * Improve default cipher suites used with the NSS plugin (#16262) + * Add NSS Preferences plugin which allows the SSL/TLS Versions and + cipher suites to be configured (#8061) * Fix a bug that prevented plugin to load when compiled without GnuTLS.
- * Fix handling of Self-Signed SSL/TLS Certificates when using the NSS
version 2.10.10 (10/22/14):
* Check the basic constraints extension when validating SSL/TLS
--- a/libpurple/pluginpref.c Tue Nov 04 22:18:30 2014 -0800
+++ b/libpurple/pluginpref.c Wed Nov 05 13:22:39 2014 -0500
@@ -154,8 +154,6 @@
tmp = g_list_delete_link(tmp, tmp);
tmp = g_list_delete_link(tmp, tmp);
- g_list_free(pref->choices);
--- a/libpurple/plugins/ssl/Makefile.am Tue Nov 04 22:18:30 2014 -0800
+++ b/libpurple/plugins/ssl/Makefile.am Wed Nov 05 13:22:39 2014 -0500
@@ -6,9 +6,11 @@
ssl_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
ssl_gnutls_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
ssl_nss_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+nss_prefs_la_LDFLAGS = -module -avoid-version @@ -17,16 +19,19 @@
ssl_gnutls_la_SOURCES = ssl-gnutls.c
ssl_nss_la_SOURCES = ssl-nss.c
+nss_prefs_la_SOURCES = nss-prefs.c ssl_la_LIBADD = @PURPLE_LIBS@
ssl_gnutls_la_LIBADD = @PURPLE_LIBS@ $(GNUTLS_LIBS)
ssl_nss_la_LIBADD = @PURPLE_LIBS@ $(NSS_LIBS)
+nss_prefs_la_LIBADD = $(GLIB_LIBS) $(NSS_LIBS) @@ -39,4 +44,5 @@
ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
ssl_nss_la_CFLAGS = $(AM_CPPFLAGS) $(NSS_CFLAGS)
+nss_prefs_la_CFLAGS = $(AM_CPPFLAGS) $(NSS_CFLAGS) --- a/libpurple/plugins/ssl/Makefile.mingw Tue Nov 04 22:18:30 2014 -0800
+++ b/libpurple/plugins/ssl/Makefile.mingw Wed Nov 05 13:22:39 2014 -0500
@@ -13,6 +13,7 @@
TARGET_GNUTLS = ssl-gnutls
+TARGET_NSSPREEFS = nss-prefs @@ -39,10 +40,12 @@
-C_SRC_GNUTLS = ssl-gnutls.c
+C_SRC_GNUTLS = ssl-gnutls.c +C_SRC_NSSPREFS = nss-prefs.c OBJECTS = $(C_SRC:%.c=%.o)
OBJECTS_NSS = $(C_SRC_NSS:%.c=%.o)
OBJECTS_GNUTLS = $(C_SRC_GNUTLS:%.c=%.o)
+OBJECTS_NSSPREFS = $(C_SRC_NSSPREFS:%.c=%.o) @@ -66,12 +69,13 @@
.PHONY: all install clean
-all: $(TARGET).dll $(TARGET_NSS).dll $(TARGET_GNUTLS).dll
+all: $(TARGET).dll $(TARGET_NSS).dll $(TARGET_GNUTLS).dll $(TARGET_NSSPREFS).dll install: all $(PURPLE_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_DIR)
cp $(TARGET).dll $(PURPLE_INSTALL_PLUGINS_DIR)
cp $(TARGET_NSS).dll $(PURPLE_INSTALL_PLUGINS_DIR)
cp $(TARGET_GNUTLS).dll $(PURPLE_INSTALL_PLUGINS_DIR)
+ cp $(TARGET_NSSPREFS).dll $(PURPLE_INSTALL_PLUGINS_DIR) $(OBJECTS) $(OBJECTS_NSS) $(OBJECTS_GNUTLS): $(PURPLE_CONFIG_H)
@@ -87,10 +91,13 @@
$(TARGET_GNUTLS).dll: $(PURPLE_DLL) $(OBJECTS_GNUTLS)
$(CC) -shared $(OBJECTS_GNUTLS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_GNUTLS).dll
+$(TARGET_NSSPREFS).dll: $(PURPLE_DLL) $(OBJECTS_NSSPREFS) + $(CC) -shared $(OBJECTS_NSSPREFS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_NSSPREFS).dll - rm -f $(OBJECTS) $(OBJECTS_NSS) $(TARGET).dll $(TARGET_NSS).dll $(TARGET_GNUTLS).dll
+ rm -f $(OBJECTS) $(OBJECTS_NSS) $(OBJECTS_GNUTLS) $(OBJECTS_NSSPREFS) $(TARGET).dll $(TARGET_NSS).dll $(TARGET_GNUTLS).dll $(TARGET_NSSPREFS).dll include $(PIDGIN_COMMON_TARGETS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/ssl/nss-prefs.c Wed Nov 05 13:22:39 2014 -0500
@@ -0,0 +1,529 @@
+ * Plugin to configure NSS + * Copyright (C) 2014, Daniel Atallah <datallah@pidgin.im> + * This program 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 2 of the + * License, or (at your option) any later version. + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +/* WINDDK_BUILD is defined because the checks around usage of + * intrisic functions are wrong in nspr */ +/* There's a bug in some versions of this header that requires that some of + the headers above be included first. This is true for at least libnss +#define PLUGIN_ID "core-nss_prefs" +#define PREF_BASE "/plugins/core/nss_prefs" +#define CIPHERS_PREF PREF_BASE "/cipher_list" +#define CIPHER_TMP_ROOT PREF_BASE "/ciphers_dummy_ui" +#define CIPHER_TMP CIPHER_TMP_ROOT "/0x%04x" +#define MIN_TLS PREF_BASE "/min_tls" +#define MAX_TLS PREF_BASE "/max_tls" +static PurplePlugin *handle = NULL; +static GList *tmp_prefs = NULL; +static GList *default_ciphers = NULL; +#if NSS_VMAJOR > 3 || ( NSS_VMAJOR == 3 && NSS_VMINOR >= 14 ) +static SSLVersionRange *default_versions = NULL; +static gchar *get_error_text(void) + PRInt32 len = PR_GetErrorTextLength(); + ret = g_malloc(len + 1); + len = PR_GetErrorText(ret); +static GList *get_current_cipher_list(gboolean force_default) { + GList *conf_ciphers = NULL; + conf_ciphers = purple_prefs_get_string_list(CIPHERS_PREF); + /* If we don't have any specifically configured ciphers, use the + * a copy of the defaults */ + for(tmp = default_ciphers; tmp; tmp = tmp->next) { + conf_ciphers = g_list_prepend(conf_ciphers, g_strdup(tmp->data)); +enable_ciphers(gboolean force_default) { + const PRUint16 *cipher; + GList *conf_ciphers, *tmp; + conf_ciphers = get_current_cipher_list(force_default); + /** First disable everything */ + for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher) { + rv = SSL_CipherPrefSetDefault(*cipher, PR_FALSE); + if (rv != SECSuccess) { + gchar *error_msg = get_error_text(); + purple_debug_warning("nss-prefs", + "Unable to disable 0x%04x: %s\n", + for (tmp = conf_ciphers; tmp; tmp = g_list_delete_link(tmp, tmp)) { + guint64 parsed = g_ascii_strtoull(tmp->data, NULL, 16); + if (parsed == 0 || parsed > PR_UINT16_MAX) { + purple_debug_error("nss-prefs", + "Cipher '%s' is not valid.\n", + (const char *) tmp->data); + rv = SSL_CipherPrefSetDefault((PRUint16) parsed, PR_TRUE); + if (rv != SECSuccess) { + gchar *error_msg = get_error_text(); + purple_debug_warning("nss-prefs", + "Unable to enable 0x%04x: %s\n", + purple_debug_info("nss-prefs", + "Enabled Cipher 0x%04x.\n", (PRUint16) parsed); +static void set_cipher_pref(const char *pref, PurplePrefType type, + gconstpointer value, gpointer user_data) { + const PRUint16 *cipher = user_data; + GList *conf_ciphers, *tmp; + gboolean enabled = GPOINTER_TO_INT(value); + gboolean found = FALSE; + purple_debug_info("nss-prefs", + "%s pref for Cipher 0x%04x.\n", + enabled ? "Adding" : "Removing", *cipher); + conf_ciphers = get_current_cipher_list(FALSE); + for (tmp = conf_ciphers; tmp; tmp = tmp->next) { + guint64 parsed = g_ascii_strtoull(tmp->data, NULL, 16); + if (parsed == 0 || parsed > PR_UINT16_MAX) { + purple_debug_error("nss-prefs", + "Cipher '%s' is not valid to set_cipher_pref.\n", + (const char *) tmp->data); + if (parsed == *cipher) { + conf_ciphers = g_list_delete_link(conf_ciphers, tmp); + conf_ciphers = g_list_prepend(conf_ciphers, + g_strdup_printf("0x%04x", *cipher)); + purple_debug_info("nss-prefs", + "Unable to find 0x%04x to disable.\n", + purple_prefs_set_string_list(CIPHERS_PREF, conf_ciphers); + for (tmp = conf_ciphers; tmp; tmp = g_list_delete_link(tmp, tmp)) { +static void set_versions(gboolean force_default) { +#if NSS_VMAJOR > 3 || ( NSS_VMAJOR == 3 && NSS_VMINOR >= 14 ) + SSLVersionRange supported, enabled; + /* Get the ranges of supported and enabled SSL versions */ + if ((SSL_VersionRangeGetSupported(ssl_variant_stream, &supported) == SECSuccess) && + (SSL_VersionRangeGetDefault(ssl_variant_stream, &enabled) == SECSuccess)) { + /* Store the defaults if this is the first time we've encountered them */ + if (!default_versions) { + default_versions = g_new0(SSLVersionRange, 1); + default_versions->min = enabled.min; + default_versions->max = enabled.max; + tmp = default_versions->min; + tmp = purple_prefs_get_int(MIN_TLS); + tmp = default_versions->max; + tmp = purple_prefs_get_int(MAX_TLS); + if (SSL_VersionRangeSetDefault(ssl_variant_stream, &enabled) == SECSuccess) { + purple_debug_info("nss-prefs", "Changed allowed TLS versions to " + "0x%04hx through 0x%04hx\n", enabled.min, enabled.max); + purple_debug_error("nss-prefs", "Error setting allowed TLS versions to " + "0x%04hx through 0x%04hx\n", enabled.min, enabled.max); + purple_debug_error("nss-prefs", "Unable set SSL/TLS Versions\n"); +#endif /* NSS >= 3.14 */ +static void set_version_pref(const char *pref, PurplePrefType type, + gconstpointer value, gpointer user_data) { +/* This is horrible, but is the only way I can think of to tie into the + * prefs UI. Add a bunch of temporary prefs that will be used to set + * the prefs list. They'll get cleaned up when the plugin is unloaded*/ +static void init_tmp_prefs(void) { + GList *conf_ciphers, *tmp; + const PRUint16 *cipher; + conf_ciphers = get_current_cipher_list(FALSE); + purple_prefs_add_none(CIPHER_TMP_ROOT); + for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher) { + gboolean found = FALSE; + gchar *pref_name = g_strdup_printf(CIPHER_TMP, *cipher); + tmp_prefs = g_list_prepend(tmp_prefs, pref_name); + guint64 parsed = g_ascii_strtoull(tmp->data, NULL, 16); + if (parsed == 0 || parsed > PR_UINT16_MAX) { + purple_debug_error("nss-prefs", + "Cipher '%s' is not valid to init_tmp_pref.\n", + (const char *) tmp->data); + if (parsed == *cipher) { + /** Remove the entry since we're done with it */ + conf_ciphers = g_list_delete_link(conf_ciphers, tmp); + purple_prefs_add_bool(pref_name, found); + purple_prefs_set_bool(pref_name, found); + purple_prefs_connect_callback(handle, pref_name, + set_cipher_pref, (void *) cipher); + tmp_prefs = g_list_reverse(tmp_prefs); + for (tmp = conf_ciphers; tmp; tmp = g_list_delete_link(tmp, tmp)) { +static PurplePluginPrefFrame * +get_plugin_pref_frame(PurplePlugin *plugin) { + PurplePluginPrefFrame *frame; + PurplePluginPref *ppref; +#if NSS_VMAJOR > 3 || ( NSS_VMAJOR == 3 && NSS_VMINOR >= 14 ) + SSLVersionRange supported, enabled; +#endif /* NSS >= 3.14 */ + frame = purple_plugin_pref_frame_new(); + ppref = purple_plugin_pref_new_with_label(_("TLS/SSL Versions")); + purple_plugin_pref_frame_add(frame, ppref); +#if NSS_VMAJOR > 3 || ( NSS_VMAJOR == 3 && NSS_VMINOR >= 14 ) + /* Get the ranges of supported and enabled SSL versions */ + if ((SSL_VersionRangeGetSupported(ssl_variant_stream, &supported) == SECSuccess) && + (SSL_VersionRangeGetDefault(ssl_variant_stream, &enabled) == SECSuccess)) { + PurplePluginPref *ppref_max; + ppref = purple_plugin_pref_new_with_name_and_label(MIN_TLS, + purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE); + ppref_max = purple_plugin_pref_new_with_name_and_label(MAX_TLS, + purple_plugin_pref_set_type(ppref_max, PURPLE_PLUGIN_PREF_CHOICE); + for (tmp_version = supported.min; tmp_version <= supported.max; tmp_version++) { + case SSL_LIBRARY_VERSION_2: + ver = g_strdup(_("SSL 2")); + case SSL_LIBRARY_VERSION_3_0: + ver = g_strdup(_("SSL 3")); + case SSL_LIBRARY_VERSION_TLS_1_0: + ver = g_strdup(_("TLS 1.0")); + case SSL_LIBRARY_VERSION_TLS_1_1: + ver = g_strdup(_("TLS 1.1")); + case SSL_LIBRARY_VERSION_TLS_1_2: + ver = g_strdup(_("TLS 1.2")); + case SSL_LIBRARY_VERSION_TLS_1_3: + ver = g_strdup(_("TLS 1.3")); + ver = g_strdup_printf("0x%04hx", tmp_version); + purple_plugin_pref_add_choice(ppref, ver, GINT_TO_POINTER((gint) tmp_version)); + purple_plugin_pref_add_choice(ppref_max, ver, GINT_TO_POINTER((gint) tmp_version)); + purple_plugin_pref_frame_add(frame, ppref); + purple_plugin_pref_frame_add(frame, ppref_max); + /* TODO: look into how to do this for older versions? */ + ppref = purple_plugin_pref_new_with_label(_("Not Supported for NSS < 3.14")); + purple_plugin_pref_frame_add(frame, ppref); +#endif /* NSS >= 3.14 */ + ppref = purple_plugin_pref_new_with_label(_("Ciphers")); + purple_plugin_pref_frame_add(frame, ppref); + offset = strlen(CIPHER_TMP_ROOT) + 1; + for (tmp = tmp_prefs; tmp; tmp = tmp->next) { + guint64 parsed = g_ascii_strtoull( (char *) tmp->data + offset, + SSLCipherSuiteInfo info; + if (parsed == 0 || parsed > PR_UINT16_MAX) { + purple_debug_error("nss-prefs", + "Cipher '%s' is not valid to build pref frame.\n", + (const char *) tmp->data + offset); + cipher = (PRUint16) parsed; + rv = SSL_GetCipherSuiteInfo(cipher, &info, (int)(sizeof info)); + if (rv != SECSuccess) { + gchar *error_msg = get_error_text(); + purple_debug_warning("nss-prefs", + "SSL_GetCipherSuiteInfo didn't like value 0x%04x: %s\n", + escaped_name = g_strdup_printf("%s (0x%04x)", + info.cipherSuiteName, cipher); + /** Escape the _ for the label */ + split = g_strsplit(escaped_name, "_", -1); + escaped_name = g_strjoinv("__", split); + ppref = purple_plugin_pref_new_with_name_and_label( + (const char *) tmp->data, escaped_name); + purple_plugin_pref_frame_add(frame, ppref); +plugin_load(PurplePlugin *plugin) { + const PRUint16 *cipher; + default_ciphers = NULL; + for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher) { + SECStatus rv = SSL_CipherPrefGetDefault(*cipher, &enabled); + if (rv == SECSuccess && enabled) { + default_ciphers = g_list_prepend(default_ciphers, + g_strdup_printf("0x%04x", *cipher)); + purple_prefs_connect_callback(handle, MIN_TLS, + set_version_pref, GINT_TO_POINTER(FALSE)); + purple_prefs_connect_callback(handle, MAX_TLS, + set_version_pref, GINT_TO_POINTER(TRUE)); +plugin_unload(PurplePlugin *plugin) { + /* Remove the temporary prefs */ + purple_prefs_remove(CIPHER_TMP_ROOT); + g_free(tmp_prefs->data); + tmp_prefs = g_list_delete_link(tmp_prefs, tmp_prefs); + /* Restore the original ciphers */ + while (default_ciphers) { + g_free(default_ciphers->data); + default_ciphers = g_list_delete_link(default_ciphers, +#if NSS_VMAJOR > 3 || ( NSS_VMAJOR == 3 && NSS_VMINOR >= 14 ) + g_free(default_versions); + default_versions = NULL; +static PurplePluginUiInfo prefs_info = { + 0, /* page_num (Reserved) */ + NULL, /* frame (Reserved) */ +static PurplePluginInfo info = { + PURPLE_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ + N_("NSS Preferences"), /**< name */ + DISPLAY_VERSION, /**< version */ + N_("Configure Ciphers and other Settings for " + "the NSS SSL/TLS Plugin"), + N_("Configure Ciphers and other Settings for " + "the NSS SSL/TLS Plugin"), + "Daniel Atallah <datallah@pidgin.im>", /**< author */ + PURPLE_WEBSITE, /**< homepage */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< extra_info */ + &prefs_info, /**< prefs_info */ +init_plugin(PurplePlugin *plugin) { + info.dependencies = g_list_prepend(info.dependencies, "ssl-nss"); + purple_prefs_add_none(PREF_BASE); + purple_prefs_add_string_list(CIPHERS_PREF, NULL); + purple_prefs_add_int(MIN_TLS, 0); + purple_prefs_add_int(MAX_TLS, 0); +PURPLE_INIT_PLUGIN(nss_prefs, init_plugin, info) --- a/libpurple/plugins/ssl/ssl-nss.c Tue Nov 04 22:18:30 2014 -0800
+++ b/libpurple/plugins/ssl/ssl-nss.c Wed Nov 05 13:22:39 2014 -0500
@@ -197,7 +197,6 @@
const PRUint16 suite = *cipher;
rv = SSL_CipherPrefGetDefault(suite, &enabled);
--- a/pidgin/win32/nsis/pidgin-installer.nsi Tue Nov 04 22:18:30 2014 -0800
+++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Nov 05 13:22:39 2014 -0500
@@ -593,6 +593,7 @@
Delete "$INSTDIR\plugins\markerline.dll"
Delete "$INSTDIR\plugins\newline.dll"
Delete "$INSTDIR\plugins\notify.dll"
+ Delete "$INSTDIR\plugins\nss-prefs.dll" Delete "$INSTDIR\plugins\offlinemsg.dll"
Delete "$INSTDIR\plugins\perl.dll"
Delete "$INSTDIR\plugins\pidginrc.dll"