pidgin/pidgin

Bump the versions for the next round of development
release-2.x.y
2021-05-13, Gary Kramlich
47988aaeaf57
Bump the versions for the next round of development

Testing Done:
Configured and built.

Reviewed at https://reviews.imfreedom.org/r/642/
/*
* 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
* 02111-1301, USA.
*/
#include "internal.h"
#include "debug.h"
#include "plugin.h"
#include "version.h"
#ifdef _WIN32
# ifndef HAVE_LONG_LONG
#define HAVE_LONG_LONG
/* WINDDK_BUILD is defined because the checks around usage of
* intrisic functions are wrong in nspr */
#define WINDDK_BUILD
# endif
#endif
#include <nspr.h>
#include <nss.h>
#include <nssb64.h>
#include <ocsp.h>
#include <pk11func.h>
#include <prio.h>
#include <secerr.h>
#include <secmod.h>
#include <ssl.h>
#include <sslerr.h>
#include <sslproto.h>
/* 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
3.15.4. */
#include <certdb.h>
#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;
#endif
static gchar *get_error_text(void)
{
PRInt32 len = PR_GetErrorTextLength();
gchar *ret = NULL;
if (len > 0) {
ret = g_malloc(len + 1);
len = PR_GetErrorText(ret);
ret[len] = '\0';
}
return ret;
}
static GList *get_current_cipher_list(gboolean force_default) {
GList *conf_ciphers = NULL;
if (!force_default) {
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 */
if (!conf_ciphers) {
GList *tmp;
for(tmp = default_ciphers; tmp; tmp = tmp->next) {
conf_ciphers = g_list_prepend(conf_ciphers, g_strdup(tmp->data));
}
}
return conf_ciphers;
}
static void
enable_ciphers(gboolean force_default) {
const PRUint16 *cipher;
GList *conf_ciphers, *tmp;
SECStatus rv;
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",
*cipher, error_msg);
g_free(error_msg);
}
}
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);
g_free(tmp->data);
continue;
}
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",
*cipher, error_msg);
g_free(error_msg);
}
purple_debug_info("nss-prefs",
"Enabled Cipher 0x%04x.\n", (PRUint16) parsed);
g_free(tmp->data);
}
}
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) {
if (!enabled) {
g_free(tmp->data);
conf_ciphers = g_list_delete_link(conf_ciphers, tmp);
}
found = TRUE;
break;
}
}
if (!found) {
if (enabled) {
conf_ciphers = g_list_prepend(conf_ciphers,
g_strdup_printf("0x%04x", *cipher));
} else {
purple_debug_info("nss-prefs",
"Unable to find 0x%04x to disable.\n",
*cipher);
}
}
purple_prefs_set_string_list(CIPHERS_PREF, conf_ciphers);
for (tmp = conf_ciphers; tmp; tmp = g_list_delete_link(tmp, tmp)) {
g_free(tmp->data);
}
enable_ciphers(FALSE);
}
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)) {
PRUint16 tmp;
/* 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;
}
if (force_default) {
tmp = default_versions->min;
} else {
tmp = purple_prefs_get_int(MIN_TLS);
}
if (tmp > 0) {
enabled.min = tmp;
}
if (force_default) {
tmp = default_versions->max;
} else {
tmp = purple_prefs_get_int(MAX_TLS);
}
if (tmp > 0) {
enabled.max = tmp;
}
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);
} else {
purple_debug_error("nss-prefs", "Error setting allowed TLS versions to "
"0x%04hx through 0x%04hx\n", enabled.min, enabled.max);
}
}
#else
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) {
set_versions(FALSE);
}
/* 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;
if (tmp_prefs) {
return;
}
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);
tmp = conf_ciphers;
while (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 to init_tmp_pref.\n",
(const char *) tmp->data);
}
if (parsed == *cipher) {
found = TRUE;
/** Remove the entry since we're done with it */
g_free(tmp->data);
conf_ciphers = g_list_delete_link(conf_ciphers, tmp);
break;
}
tmp = tmp->next;
}
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)) {
g_free(tmp->data);
}
}
static PurplePluginPrefFrame *
get_plugin_pref_frame(PurplePlugin *plugin) {
PurplePluginPrefFrame *frame;
PurplePluginPref *ppref;
int offset;
GList *tmp;
#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)) {
PRUint16 tmp_version;
PurplePluginPref *ppref_max;
ppref = purple_plugin_pref_new_with_name_and_label(MIN_TLS,
_("Minimum Version"));
purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
ppref_max = purple_plugin_pref_new_with_name_and_label(MAX_TLS,
_("Maximum Version"));
purple_plugin_pref_set_type(ppref_max, PURPLE_PLUGIN_PREF_CHOICE);
for (tmp_version = supported.min; tmp_version <= supported.max; tmp_version++) {
gchar *ver;
switch (tmp_version) {
case SSL_LIBRARY_VERSION_2:
ver = g_strdup(_("SSL 2"));
break;
case SSL_LIBRARY_VERSION_3_0:
ver = g_strdup(_("SSL 3"));
break;
case SSL_LIBRARY_VERSION_TLS_1_0:
ver = g_strdup(_("TLS 1.0"));
break;
case SSL_LIBRARY_VERSION_TLS_1_1:
ver = g_strdup(_("TLS 1.1"));
break;
#ifdef SSL_LIBRARY_VERSION_TLS_1_2
case SSL_LIBRARY_VERSION_TLS_1_2:
ver = g_strdup(_("TLS 1.2"));
break;
#endif
#ifdef SSL_LIBRARY_VERSION_TLS_1_3
case SSL_LIBRARY_VERSION_TLS_1_3:
ver = g_strdup(_("TLS 1.3"));
break;
#endif
default:
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));
g_free(ver);
}
purple_plugin_pref_frame_add(frame, ppref);
purple_plugin_pref_frame_add(frame, ppref_max);
}
#else
/* 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);
init_tmp_prefs();
offset = strlen(CIPHER_TMP_ROOT) + 1;
for (tmp = tmp_prefs; tmp; tmp = tmp->next) {
guint64 parsed = g_ascii_strtoull( (char *) tmp->data + offset,
NULL, 16);
PRUint16 cipher;
SECStatus rv;
gchar **split;
gchar *escaped_name;
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);
continue;
}
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",
cipher, error_msg);
g_free(error_msg);
continue;
}
escaped_name = g_strdup_printf("%s (0x%04x)",
info.cipherSuiteName, cipher);
/** Escape the _ for the label */
split = g_strsplit(escaped_name, "_", -1);
g_free(escaped_name);
escaped_name = g_strjoinv("__", split);
g_strfreev(split);
ppref = purple_plugin_pref_new_with_name_and_label(
(const char *) tmp->data, escaped_name);
g_free(escaped_name);
purple_plugin_pref_frame_add(frame, ppref);
}
return frame;
}
static gboolean
plugin_load(PurplePlugin *plugin) {
const PRUint16 *cipher;
handle = plugin;
tmp_prefs = NULL;
default_ciphers = NULL;
for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher) {
PRBool enabled;
SECStatus rv = SSL_CipherPrefGetDefault(*cipher, &enabled);
if (rv == SECSuccess && enabled) {
default_ciphers = g_list_prepend(default_ciphers,
g_strdup_printf("0x%04x", *cipher));
}
}
enable_ciphers(FALSE);
set_versions(FALSE);
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));
return TRUE;
}
static gboolean
plugin_unload(PurplePlugin *plugin) {
/* Remove the temporary prefs */
if (tmp_prefs) {
purple_prefs_remove(CIPHER_TMP_ROOT);
while (tmp_prefs) {
g_free(tmp_prefs->data);
tmp_prefs = g_list_delete_link(tmp_prefs, tmp_prefs);
}
}
/* Restore the original ciphers */
enable_ciphers(TRUE);
while (default_ciphers) {
g_free(default_ciphers->data);
default_ciphers = g_list_delete_link(default_ciphers,
default_ciphers);
}
set_versions(TRUE);
#if NSS_VMAJOR > 3 || ( NSS_VMAJOR == 3 && NSS_VMINOR >= 14 )
g_free(default_versions);
default_versions = NULL;
#endif
return TRUE;
}
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
0, /* page_num (Reserved) */
NULL, /* frame (Reserved) */
/* Padding */
NULL,
NULL,
NULL,
NULL
};
static PurplePluginInfo info = {
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD, /**< type */
NULL, /**< ui_requirement */
0, /**< flags */
NULL, /**< dependencies */
PURPLE_PRIORITY_DEFAULT, /**< priority */
PLUGIN_ID, /**< id */
N_("NSS Preferences"), /**< name */
DISPLAY_VERSION, /**< version */
/** summary */
N_("Configure Ciphers and other Settings for "
"the NSS SSL/TLS Plugin"),
/** description */
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, /**< destroy */
NULL, /**< ui_info */
NULL, /**< extra_info */
&prefs_info, /**< prefs_info */
NULL,
/* Padding */
NULL,
NULL,
NULL,
NULL
};
static void
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)