summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/wee-command.c235
-rw-r--r--src/core/wee-completion.c44
-rw-r--r--src/core/wee-config.c10
-rw-r--r--src/core/wee-debug.c8
-rw-r--r--src/core/wee-eval.c46
-rw-r--r--src/core/wee-network.c34
-rw-r--r--src/core/wee-network.h5
-rw-r--r--src/core/wee-secure.c1295
-rw-r--r--src/core/wee-secure.h70
-rw-r--r--src/core/wee-string.c96
-rw-r--r--src/core/wee-string.h4
-rw-r--r--src/core/wee-upgrade.c4
-rw-r--r--src/core/weechat.c18
15 files changed, 1811 insertions, 61 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9122f4234..0a4032e01 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -37,6 +37,7 @@ wee-list.c wee-list.h
wee-log.c wee-log.h
wee-network.c wee-network.h
wee-proxy.c wee-proxy.h
+wee-secure.c wee-secure.h
wee-string.c wee-string.h
wee-upgrade.c wee-upgrade.h
wee-upgrade-file.c wee-upgrade-file.h
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 297a044b5..46f6f3e51 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -55,6 +55,8 @@ lib_weechat_core_a_SOURCES = weechat.c \
wee-network.h \
wee-proxy.c \
wee-proxy.h \
+ wee-secure.c \
+ wee-secure.h \
wee-string.c \
wee-string.h \
wee-upgrade.c \
diff --git a/src/core/wee-command.c b/src/core/wee-command.c
index 6f8577488..0ed5eb6e1 100644
--- a/src/core/wee-command.c
+++ b/src/core/wee-command.c
@@ -45,6 +45,7 @@
#include "wee-list.h"
#include "wee-log.h"
#include "wee-proxy.h"
+#include "wee-secure.h"
#include "wee-string.h"
#include "wee-upgrade.h"
#include "wee-utf8.h"
@@ -4482,6 +4483,168 @@ COMMAND_CALLBACK(save)
}
/*
+ * Displays a secured data.
+ */
+
+void
+command_secure_display_data (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) hashtable;
+ (void) value;
+
+ if (key)
+ gui_chat_printf (NULL, " %s", key);
+}
+
+/*
+ * Callback for command "/secure": manage secured data
+ */
+
+COMMAND_CALLBACK(secure)
+{
+ int passphrase_was_set, count_encrypted;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) buffer;
+
+ /* list of secured data */
+ if (argc == 1)
+ {
+ secure_buffer_open ();
+ return WEECHAT_RC_OK;
+ }
+
+ count_encrypted = secure_hashtable_data_encrypted->items_count;
+
+ /* decrypt data still encrypted */
+ if (string_strcasecmp (argv[1], "decrypt") == 0)
+ {
+ COMMAND_MIN_ARGS(3, "secure decrypt");
+ if (count_encrypted == 0)
+ {
+ gui_chat_printf (NULL, _("There is no encrypted data"));
+ return WEECHAT_RC_OK;
+ }
+ if (strcmp (argv[2], "-discard") == 0)
+ {
+ hashtable_remove_all (secure_hashtable_data_encrypted);
+ gui_chat_printf (NULL, _("Encrypted data deleted"));
+ return WEECHAT_RC_OK;
+ }
+ if (secure_decrypt_data_not_decrypted (argv_eol[2]) > 0)
+ {
+ gui_chat_printf (NULL,
+ _("Encrypted data has been successfully decrypted"));
+ if (secure_passphrase)
+ free (secure_passphrase);
+ secure_passphrase = strdup (argv_eol[2]);
+ }
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%sFailed to decrypt data (wrong passphrase?)"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]);
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ if (count_encrypted > 0)
+ {
+ gui_chat_printf (NULL,
+ _("%sYou must decrypt data still encrypted before "
+ "doing any operation on secured data or passphrase"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]);
+ return WEECHAT_RC_OK;
+ }
+
+ /* set the passphrase */
+ if (string_strcasecmp (argv[1], "passphrase") == 0)
+ {
+ COMMAND_MIN_ARGS(3, "secure passphrase");
+ passphrase_was_set = 0;
+ if (secure_passphrase)
+ {
+ free (secure_passphrase);
+ secure_passphrase = NULL;
+ passphrase_was_set = 1;
+ }
+ if (strcmp (argv[2], "-delete") == 0)
+ {
+ gui_chat_printf (NULL,
+ (passphrase_was_set) ?
+ _("Passphrase deleted") : _("Passphrase is not set"));
+ if (passphrase_was_set)
+ {
+ if (secure_hashtable_data->items_count > 0)
+ command_save_file (secure_config_file);
+ secure_buffer_display ();
+ }
+ }
+ else
+ {
+ secure_passphrase = strdup (argv_eol[2]);
+ gui_chat_printf (NULL,
+ (passphrase_was_set) ?
+ _("Passphrase changed") : _("Passphrase added"));
+ if (secure_hashtable_data->items_count > 0)
+ command_save_file (secure_config_file);
+ secure_buffer_display ();
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* set a secured data */
+ if (string_strcasecmp (argv[1], "set") == 0)
+ {
+ COMMAND_MIN_ARGS(4, "secure set");
+ hashtable_set (secure_hashtable_data, argv[2], argv_eol[3]);
+ gui_chat_printf (NULL, _("Secured data \"%s\" set"), argv[2]);
+ command_save_file (secure_config_file);
+ secure_buffer_display ();
+ return WEECHAT_RC_OK;
+ }
+
+ /* delete a secured data */
+ if (string_strcasecmp (argv[1], "del") == 0)
+ {
+ COMMAND_MIN_ARGS(3, "secure del");
+ if (hashtable_has_key (secure_hashtable_data, argv[2]))
+ {
+ hashtable_remove (secure_hashtable_data, argv[2]);
+ gui_chat_printf (NULL, _("Secured data \"%s\" deleted"), argv[2]);
+ command_save_file (secure_config_file);
+ secure_buffer_display ();
+ }
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%sSecured data \"%s\" not found"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ argv[2]);
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* toggle values on secured data buffer */
+ if (string_strcasecmp (argv[1], "toggle_values") == 0)
+ {
+ if (secure_buffer)
+ {
+ secure_buffer_display_values ^= 1;
+ secure_buffer_display ();
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
* Displays a configuration section.
*/
@@ -5159,6 +5322,14 @@ COMMAND_CALLBACK(upgrade)
return WEECHAT_RC_OK;
}
+ /*
+ * set passphrase in environment var, so that it will not be asked to user
+ * when starting the new binary
+ */
+ if (secure_passphrase)
+ setenv (SECURE_ENV_PASSPHRASE, secure_passphrase, 1);
+
+ /* execute binary */
exec_args[0] = ptr_binary;
exec_args[3] = strdup (weechat_home);
execvp (exec_args[0], exec_args);
@@ -5166,7 +5337,8 @@ COMMAND_CALLBACK(upgrade)
/* this code should not be reached if execvp is OK */
string_iconv_fprintf (stderr, "\n\n*****\n");
string_iconv_fprintf (stderr,
- _("***** Error: exec failed (program: \"%s\"), exiting WeeChat"),
+ _("***** Error: exec failed (program: \"%s\"), "
+ "exiting WeeChat"),
exec_args[0]);
string_iconv_fprintf (stderr, "\n*****\n\n");
@@ -6634,6 +6806,55 @@ command_init ()
"saved."),
"%(config_files)|%*",
&command_save, NULL);
+ hook_command (NULL, "secure",
+ N_("manage secured data (passwords or private data encrypted "
+ "in file sec.conf)"),
+ N_("passphrase <passphrase>|-delete"
+ " || decrypt <passphrase>|-discard"
+ " || set <name> <value>"
+ " || del <name>"),
+ N_("passphrase: set or change the passphrase used for "
+ "encryption (without passphrase, data is stored as "
+ "plain text in file sec.conf)\n"
+ " -delete: delete passphrase\n"
+ " decrypt: decrypt data still encrypted (it happens only "
+ "if no passphrase was given for encrypted data on startup)\n"
+ " -discard: discard all encrypted data (WARNING: this "
+ "will clear the file sec.conf)\n"
+ " set: add or change secured data\n"
+ " del: delete secured data\n\n"
+ "Without argument, this command displays secured data "
+ "in a new buffer.\n\n"
+ "When a passphrase is used (data encrypted), it is asked "
+ "by WeeChat on startup.\n"
+ "It is possible to set environment variable \""
+ SECURE_ENV_PASSPHRASE "\" to prevent the prompt (this same "
+ "variable is used by WeeChat on /upgrade).\n\n"
+ "Secured data with format ${sec.data.xxx} can be used in:\n"
+ " - command line argument \"--run-command\"\n"
+ " - irc server options: autojoin, command, password, "
+ "sasl_{username|password}\n"
+ " - options weechat.startup.command_{before|after}_plugins\n"
+ " - command /eval.\n\n"
+ "Examples:\n"
+ " set a passphrase:\n"
+ " /secure passphrase this is my passphrase\n"
+ " encrypt freenode SASL password:\n"
+ " /secure set freenode mypassword\n"
+ " /set irc.server.freenode.sasl_password "
+ "\"${sec.data.freenode}\"\n"
+ " encrypt oftc password for nickserv:\n"
+ " /secure set oftc mypassword\n"
+ " /set irc.server.oftc.command \"/msg nickserv identify "
+ "${sec.data.oftc}\"\n"
+ " alias to ghost \"mynick\":\n"
+ " /alias ghost /eval /msg -server freenode nickserv "
+ "ghost mynick ${sec.data.freenode}"),
+ "passphrase -delete"
+ " || decrypt -discard"
+ " || set %(secured_data)"
+ " || del %(secured_data)",
+ &command_secure, NULL);
hook_command (NULL, "set",
N_("set config options"),
N_("[<option> [<value>]] || diff [<option> [<option>...]]"),
@@ -6864,12 +7085,16 @@ command_init ()
void
command_exec_list (const char *command_list)
{
- char **commands, **ptr_cmd;
+ char *command_list2, **commands, **ptr_cmd;
struct t_gui_buffer *weechat_buffer;
- if (command_list && command_list[0])
+ if (!command_list || !command_list[0])
+ return;
+
+ command_list2 = eval_expression (command_list, NULL, NULL);
+ if (command_list2 && command_list2[0])
{
- commands = string_split_command (command_list, ';');
+ commands = string_split_command (command_list2, ';');
if (commands)
{
weechat_buffer = gui_buffer_search_main ();
@@ -6880,6 +7105,8 @@ command_exec_list (const char *command_list)
string_free_split_command (commands);
}
}
+ if (command_list2)
+ free (command_list2);
}
/*
diff --git a/src/core/wee-completion.c b/src/core/wee-completion.c
index 3639d101b..65f31632c 100644
--- a/src/core/wee-completion.c
+++ b/src/core/wee-completion.c
@@ -39,6 +39,7 @@
#include "wee-hook.h"
#include "wee-list.h"
#include "wee-proxy.h"
+#include "wee-secure.h"
#include "wee-string.h"
#include "../gui/gui-completion.h"
#include "../gui/gui-bar.h"
@@ -1298,6 +1299,46 @@ completion_list_add_layouts_names_cb (void *data,
}
/*
+ * Adds a secured data to completion list.
+ */
+
+void
+completion_list_map_add_secured_data_cb (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ /* make C compiler happy */
+ (void) hashtable;
+ (void) value;
+
+ gui_completion_list_add ((struct t_gui_completion *)data,
+ (const char *)key,
+ 0, WEECHAT_LIST_POS_SORT);
+}
+
+/*
+ * Adds secured data to completion list.
+ */
+
+int
+completion_list_add_secured_data_cb (void *data,
+ const char *completion_item,
+ struct t_gui_buffer *buffer,
+ struct t_gui_completion *completion)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) completion_item;
+ (void) buffer;
+
+ hashtable_map (secure_hashtable_data,
+ &completion_list_map_add_secured_data_cb,
+ completion);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
* Adds hooks for completions done by WeeChat core.
*/
@@ -1392,4 +1433,7 @@ completion_init ()
hook_completion (NULL, "layouts_names",
N_("names of layouts"),
&completion_list_add_layouts_names_cb, NULL);
+ hook_completion (NULL, "secured_data",
+ N_("names of secured data (file sec.conf, section data)"),
+ &completion_list_add_secured_data_cb, NULL);
}
diff --git a/src/core/wee-config.c b/src/core/wee-config.c
index b7f794713..fb86fe2ad 100644
--- a/src/core/wee-config.c
+++ b/src/core/wee-config.c
@@ -690,7 +690,7 @@ config_change_network_gnutls_ca_file (void *data,
(void) data;
(void) option;
- if (network_init_ok)
+ if (network_init_gnutls_ok)
network_set_gnutls_ca_file ();
}
@@ -1706,7 +1706,7 @@ config_weechat_filter_read_cb (void *data,
}
/*
- * Writes a filter option in WeeChat configuration file.
+ * Writes section "filter" in WeeChat configuration file.
*/
int
@@ -1873,12 +1873,14 @@ config_weechat_init_options ()
config_startup_command_after_plugins = config_file_new_option (
weechat_config_file, ptr_section,
"command_after_plugins", "string",
- N_("command executed when WeeChat starts, after loading plugins"),
+ N_("command executed when WeeChat starts, after loading plugins "
+ "(note: content is evaluated, see /help eval)"),
NULL, 0, 0, "", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
config_startup_command_before_plugins = config_file_new_option (
weechat_config_file, ptr_section,
"command_before_plugins", "string",
- N_("command executed when WeeChat starts, before loading plugins"),
+ N_("command executed when WeeChat starts, before loading plugins "
+ "(note: content is evaluated, see /help eval)"),
NULL, 0, 0, "", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
config_startup_display_logo = config_file_new_option (
weechat_config_file, ptr_section,
diff --git a/src/core/wee-debug.c b/src/core/wee-debug.c
index cf8770d24..17e645fa7 100644
--- a/src/core/wee-debug.c
+++ b/src/core/wee-debug.c
@@ -369,10 +369,8 @@ debug_hdata_map_cb (void *data, struct t_hashtable *hashtable,
gui_chat_printf (NULL,
" hdata 0x%lx: \"%s\", %d vars, %d lists:",
ptr_hdata, (const char *)key,
- hashtable_get_integer (ptr_hdata->hash_var,
- "items_count"),
- hashtable_get_integer (ptr_hdata->hash_list,
- "items_count"));
+ ptr_hdata->hash_var->items_count,
+ ptr_hdata->hash_list->items_count);
/* display lists */
hashtable_map (ptr_hdata->hash_list,
@@ -412,7 +410,7 @@ debug_hdata ()
{
int count;
- count = hashtable_get_integer (weechat_hdata, "items_count");
+ count = weechat_hdata->items_count;
gui_chat_printf (NULL, "");
gui_chat_printf (NULL, "%d hdata in memory", count);
diff --git a/src/core/wee-eval.c b/src/core/wee-eval.c
index 2fce50097..866f44f8c 100644
--- a/src/core/wee-eval.c
+++ b/src/core/wee-eval.c
@@ -33,6 +33,7 @@
#include "wee-hashtable.h"
#include "wee-hdata.h"
#include "wee-hook.h"
+#include "wee-secure.h"
#include "wee-string.h"
#include "../gui/gui-buffer.h"
#include "../gui/gui-color.h"
@@ -245,25 +246,34 @@ eval_replace_vars_cb (void *data, const char *text)
return strdup (ptr_value);
/* 2. look for name of option: if found, return this value */
- config_file_search_with_string (text, NULL, NULL, &ptr_option, NULL);
- if (ptr_option)
+ if (strncmp (text, "sec.data.", 9) == 0)
{
- switch (ptr_option->type)
+ ptr_value = hashtable_get (secure_hashtable_data, text + 9);
+ if (ptr_value)
+ return strdup (ptr_value);
+ }
+ else
+ {
+ config_file_search_with_string (text, NULL, NULL, &ptr_option, NULL);
+ if (ptr_option)
{
- case CONFIG_OPTION_TYPE_BOOLEAN:
- return strdup (CONFIG_BOOLEAN(ptr_option) ? EVAL_STR_TRUE : EVAL_STR_FALSE);
- case CONFIG_OPTION_TYPE_INTEGER:
- if (ptr_option->string_values)
- return strdup (ptr_option->string_values[CONFIG_INTEGER(ptr_option)]);
- snprintf (str_value, sizeof (str_value),
- "%d", CONFIG_INTEGER(ptr_option));
- return strdup (str_value);
- case CONFIG_OPTION_TYPE_STRING:
- return strdup (CONFIG_STRING(ptr_option));
- case CONFIG_OPTION_TYPE_COLOR:
- return strdup (gui_color_get_name (CONFIG_COLOR(ptr_option)));
- case CONFIG_NUM_OPTION_TYPES:
- return NULL;
+ switch (ptr_option->type)
+ {
+ case CONFIG_OPTION_TYPE_BOOLEAN:
+ return strdup (CONFIG_BOOLEAN(ptr_option) ? EVAL_STR_TRUE : EVAL_STR_FALSE);
+ case CONFIG_OPTION_TYPE_INTEGER:
+ if (ptr_option->string_values)
+ return strdup (ptr_option->string_values[CONFIG_INTEGER(ptr_option)]);
+ snprintf (str_value, sizeof (str_value),
+ "%d", CONFIG_INTEGER(ptr_option));
+ return strdup (str_value);
+ case CONFIG_OPTION_TYPE_STRING:
+ return strdup (CONFIG_STRING(ptr_option));
+ case CONFIG_OPTION_TYPE_COLOR:
+ return strdup (gui_color_get_name (CONFIG_COLOR(ptr_option)));
+ case CONFIG_NUM_OPTION_TYPES:
+ return NULL;
+ }
}
}
@@ -345,7 +355,7 @@ eval_replace_vars (const char *expr, struct t_hashtable *pointers,
ptr[0] = pointers;
ptr[1] = extra_vars;
- return string_replace_with_callback (expr,
+ return string_replace_with_callback (expr, "${", "}",
&eval_replace_vars_cb,
ptr,
&errors);
diff --git a/src/core/wee-network.c b/src/core/wee-network.c
index a15eea8b2..aa5ea105e 100644
--- a/src/core/wee-network.c
+++ b/src/core/wee-network.c
@@ -55,7 +55,7 @@
#include "../plugins/plugin.h"
-int network_init_ok = 0;
+int network_init_gnutls_ok = 0;
#ifdef HAVE_GNUTLS
gnutls_certificate_credentials_t gnutls_xcred; /* GnuTLS client credentials */
@@ -63,6 +63,21 @@ gnutls_certificate_credentials_t gnutls_xcred; /* GnuTLS client credentials */
/*
+ * Initializes gcrypt.
+ */
+
+void
+network_init_gcrypt ()
+{
+ if (!weechat_no_gcrypt)
+ {
+ gcry_check_version (GCRYPT_VERSION);
+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ }
+}
+
+/*
* Sets trust file with option "gnutls_ca_file".
*/
@@ -91,11 +106,11 @@ network_set_gnutls_ca_file ()
}
/*
- * Initializes network.
+ * Initializes GnuTLS.
*/
void
-network_init ()
+network_init_gnutls ()
{
#ifdef HAVE_GNUTLS
if (!weechat_no_gnutls)
@@ -121,14 +136,7 @@ network_init ()
}
#endif /* HAVE_GNUTLS */
- if (!weechat_no_gcrypt)
- {
- gcry_check_version (GCRYPT_VERSION);
- gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
- gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
- }
-
- network_init_ok = 1;
+ network_init_gnutls_ok = 1;
}
/*
@@ -138,7 +146,7 @@ network_init ()
void
network_end ()
{
- if (network_init_ok)
+ if (network_init_gnutls_ok)
{
#ifdef HAVE_GNUTLS
if (!weechat_no_gnutls)
@@ -147,7 +155,7 @@ network_end ()
gnutls_global_deinit();
}
#endif
- network_init_ok = 0;
+ network_init_gnutls_ok = 0;
}
}
diff --git a/src/core/wee-network.h b/src/core/wee-network.h
index e421db020..10294f2cb 100644
--- a/src/core/wee-network.h
+++ b/src/core/wee-network.h
@@ -40,10 +40,11 @@ struct t_network_socks5
/* auth(user/pass) (2), ... */
};
-extern int network_init_ok;
+extern int network_init_gnutls_ok;
+extern void network_init_gcrypt ();
extern void network_set_gnutls_ca_file ();
-extern void network_init ();
+extern void network_init_gnutls ();
extern void network_end ();
extern int network_pass_proxy (const char *proxy, int sock,
const char *address, int port);
diff --git a/src/core/wee-secure.c b/src/core/wee-secure.c
new file mode 100644
index 000000000..9053d76bd
--- /dev/null
+++ b/src/core/wee-secure.c
@@ -0,0 +1,1295 @@
+/*
+ * wee-secure.c - secured data configuration options (file sec.conf)
+ *
+ * Copyright (C) 2013 Sebastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat 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.
+ *
+ * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gcrypt.h>
+
+#include "weechat.h"
+#include "wee-config-file.h"
+#include "wee-hashtable.h"
+#include "wee-secure.h"
+#include "wee-string.h"
+#include "../gui/gui-buffer.h"
+#include "../gui/gui-chat.h"
+#include "../gui/gui-color.h"
+#include "../gui/gui-main.h"
+#include "../gui/gui-window.h"
+#include "../plugins/plugin.h"
+
+#define SALT_SIZE 8
+
+struct t_config_file *secure_config_file = NULL;
+struct t_config_section *secure_config_section_crypt = NULL;
+struct t_config_section *secure_config_section_data = NULL;
+
+struct t_config_option *secure_config_crypt_cipher = NULL;
+struct t_config_option *secure_config_crypt_hash_algo = NULL;
+struct t_config_option *secure_config_crypt_passphrase_file = NULL;
+struct t_config_option *secure_config_crypt_salt = NULL;
+
+/* the passphrase used to encrypt/decrypt data */
+char *secure_passphrase = NULL;
+
+/* decrypted data */
+struct t_hashtable *secure_hashtable_data = NULL;
+
+/* data still encrypted (if passphrase not set) */
+struct t_hashtable *secure_hashtable_data_encrypted = NULL;
+
+/* hash algorithms */
+char *secure_hash_algo_string[] = { "sha224", "sha256", "sha384", "sha512",
+ NULL };
+int secure_hash_algo[] = { GCRY_MD_SHA224, GCRY_MD_SHA256, GCRY_MD_SHA384,
+ GCRY_MD_SHA512 };
+
+/* ciphers */
+char *secure_cipher_string[] = { "aes128", "aes192", "aes256", NULL };
+int secure_cipher[] = { GCRY_CIPHER_AES128, GCRY_CIPHER_AES192,
+ GCRY_CIPHER_AES256 };
+
+char *secure_decrypt_error[] = { "memory", "buffer", "key", "cipher", "setkey",
+ "decrypt", "hash", "hash mismatch" };
+
+/* used only when reading sec.conf: 1 if flag __passphrase__ is enabled */
+int secure_data_encrypted = 0;
+
+/* secured data buffer */
+struct t_gui_buffer *secure_buffer = NULL;
+int secure_buffer_display_values = 0;
+
+
+/*
+ * Searches for a hash algorithm.
+ *
+ * Returns hash algorithm value (from gcrypt constant), -1 if hash algorithm is
+ * not found.
+ */
+
+int
+secure_search_hash_algo (const char *hash_algo)
+{
+ int i;
+
+ if (!hash_algo)
+ return -1;
+
+ for (i = 0; secure_hash_algo_string[i]; i++)
+ {
+ if (strcmp (secure_hash_algo_string[i], hash_algo) == 0)
+ return secure_hash_algo[i];
+ }
+
+ /* hash algorithm not found */
+ return -1;
+}
+
+/*
+ * Searches for a cipher.
+ *
+ * Returns cipher value (from gcrypt constant), -1 if cipher is not
+ * found.
+ */
+
+int
+secure_search_cipher (const char *cipher)
+{
+ int i;
+
+ if (!cipher)
+ return -1;
+
+ for (i = 0; secure_cipher_string[i]; i++)
+ {
+ if (strcmp (secure_cipher_string[i], cipher) == 0)
+ return secure_cipher[i];
+ }
+
+ /* cipher not found */
+ return -1;
+}
+
+/*
+ * Encrypts data using a hash algorithm + cipher + passphrase.
+ *
+ * Following actions are performed:
+ * 1. derive a key from the passphrase (with optional salt)
+ * 2. compute hash of data
+ * 3. store hash + data in a buffer
+ * 4. encrypt the buffer (hash + data), using the key
+ * 5. return salt + encrypted hash/data
+ *
+ * Output buffer has following content:
+ * - salt (8 bytes, used to derive a key from the passphrase)
+ * - encrypted hash(data) + data
+ *
+ * So it looks like:
+ *
+ * +----------+------------+------------------------------+
+ * | salt | hash | data |
+ * +----------+------------+------------------------------+
+ * \_ _ _ _ _/\_ _ _ _ _ _ /\_ _ _ _ _ _ _ _ _ _ _ _ _ _ _/
+ * 8 bytes N bytes variable length
+ * \_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/
+ * encrypted data
+ *
+ * Returns:
+ * 0: OK
+ * -1: not enough memory
+ * -2: key derive error
+ * -3: compute hash error
+ * -4: cipher open error
+ * -5: setkey error
+ * -6: encrypt error
+ */
+
+int
+secure_encrypt_data (const char *data, int length_data,
+ int hash_algo, int cipher, const char *passphrase,
+ char **encrypted, int *length_encrypted)
+{
+ int rc, length_salt, length_hash, length_hash_data, length_key;
+ gcry_md_hd_t *hd_md;
+ gcry_cipher_hd_t *hd_cipher;
+ unsigned char salt[SALT_SIZE], *ptr_hash, *key, *hash_and_data;
+
+ rc = -1;
+
+ hd_md = NULL;
+ hd_cipher = NULL;
+ key = NULL;
+ hash_and_data = NULL;
+
+ hd_md = malloc (sizeof (gcry_md_hd_t));
+ if (!hd_md)
+ return -1;
+ hd_cipher = malloc (sizeof (gcry_cipher_hd_t));
+ if (!hd_cipher)
+ {
+ free (hd_md);
+ return -1;
+ }
+
+ /* derive a key from the passphrase */
+ length_key = gcry_cipher_get_algo_keylen (cipher);
+ key = malloc (length_key);
+ if (!key)
+ goto encend;
+ if (CONFIG_BOOLEAN(secure_config_crypt_salt))
+ gcry_randomize (salt, SALT_SIZE, GCRY_STRONG_RANDOM);
+ else
+ {
+ length_salt = strlen (SECURE_SALT_DEFAULT);
+ if (length_salt < SALT_SIZE)
+ memset (salt, 0, SALT_SIZE);
+ memcpy (salt, SECURE_SALT_DEFAULT,
+ (length_salt <= SALT_SIZE) ? length_salt : SALT_SIZE);
+ }
+ if (gcry_kdf_derive (passphrase, strlen (passphrase),
+ GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA256,
+ salt, SALT_SIZE, 65536, length_key, key) != 0)
+ {
+ rc = -2;
+ goto encend;
+ }
+
+ /* compute hash of data */
+ if (gcry_md_open (hd_md, hash_algo, 0) != 0)
+ {
+ rc = -3;
+ goto encend;
+ }
+ length_hash = gcry_md_get_algo_dlen (hash_algo);
+ gcry_md_write (*hd_md, data, length_data);
+ ptr_hash = gcry_md_read (*hd_md, hash_algo);
+ if (!ptr_hash)
+ {
+ rc = -3;
+ goto encend;
+ }
+
+ /* build a buffer with hash + data */
+ length_hash_data = length_hash + length_data;
+ hash_and_data = malloc (length_hash_data);
+ if (!hash_and_data)
+ goto encend;
+ memcpy (hash_and_data, ptr_hash, length_hash);
+ memcpy (hash_and_data + length_hash, data, length_data);
+
+ /* encrypt hash + data */
+ if (gcry_cipher_open (hd_cipher, cipher, GCRY_CIPHER_MODE_CFB, 0) != 0)
+ {
+ rc = -4;
+ goto encend;
+ }
+ if (gcry_cipher_setkey (*hd_cipher, key, length_key) != 0)
+ {
+ rc = -5;
+ goto encend;
+ }
+ if (gcry_cipher_encrypt (*hd_cipher, hash_and_data, length_hash_data,
+ NULL, 0) != 0)
+ {
+ rc = -6;
+ goto encend;
+ }
+
+ /* create buffer and copy salt + encrypted hash/data into this buffer*/
+ *length_encrypted = SALT_SIZE + length_hash_data;
+ *encrypted = malloc (*length_encrypted);
+ if (!*encrypted)
+ goto encend;
+ memcpy (*encrypted, salt, SALT_SIZE);
+ memcpy (*encrypted + SALT_SIZE, hash_and_data, length_hash_data);
+
+ rc = 0;
+
+encend:
+ if (hd_md)
+ {
+ gcry_md_close (*hd_md);
+ free (hd_md);
+ }
+ if (hd_cipher)
+ {
+ gcry_cipher_close (*hd_cipher);
+ free (hd_cipher);
+ }
+ if (key)
+ free (key);
+ if (hash_and_data)
+ free (hash_and_data);
+
+ return rc;
+}
+
+/*
+ * Decrypts data using a hash algorithm + cipher + passphrase.
+ *
+ * The buffer must contain:
+ * - salt (8 bytes, used to derive a key from the passphrase)
+ * - encrypted hash(data) + data
+ *
+ * Following actions are performed:
+ * 1. check length of buffer (it must have at least salt + hash + some data)
+ * 2. derive a key from the passphrase using salt (at beginning of buffer)
+ * 3. decrypt hash + data in a buffer
+ * 4. compute hash of decrypted data
+ * 5. check that decrypted hash is equal to hash of data
+ * 6. return decrypted data
+ *
+ * Returns:
+ * 0: OK
+ * -1: not enough memory
+ * -2: buffer is not long enough
+ * -3: key derive error
+ * -4: cipher open error
+ * -5: setkey error
+ * -6: decrypt error
+ * -7: compute hash error
+ * -8: hash does not match the decrypted data
+ *
+ * Note: when adding a return code, change the array "secure_decrypt_error"
+ * accordingly.
+ */
+
+int
+secure_decrypt_data (const char *buffer, int length_buffer,
+ int hash_algo, int cipher, const char *passphrase,
+ char **decrypted, int *length_decrypted)
+{
+ int rc, length_hash, length_key;
+ gcry_md_hd_t *hd_md;
+ gcry_cipher_hd_t *hd_cipher;
+ unsigned char *ptr_hash, *key, *decrypted_hash_data;
+
+ rc = -1;
+
+ /* check length of buffer */
+ length_hash = gcry_md_get_algo_dlen (hash_algo);
+ if (length_buffer <= SALT_SIZE + length_hash)
+ return -2;
+
+ hd_md = NULL;
+ hd_cipher = NULL;
+ key = NULL;
+ decrypted_hash_data = NULL;
+
+ hd_md = malloc (sizeof (gcry_md_hd_t));
+ if (!hd_md)
+ return rc;
+ hd_cipher = malloc (sizeof (gcry_cipher_hd_t));
+ if (!hd_cipher)
+ {
+ free (hd_md);
+ return rc;
+ }
+
+ /* derive a key from the passphrase */
+ length_key = gcry_cipher_get_algo_keylen (cipher);
+ key = malloc (length_key);
+ if (!key)
+ goto decend;
+ if (gcry_kdf_derive (passphrase, strlen (passphrase),
+ GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA256,
+ buffer, SALT_SIZE, 65536, length_key, key) != 0)
+ {
+ rc = -3;
+ goto decend;
+ }
+
+ /* decrypt hash + data */
+ decrypted_hash_data = malloc (length_buffer - SALT_SIZE);
+ if (!decrypted_hash_data)
+ goto decend;
+ if (gcry_cipher_open (hd_cipher, cipher, GCRY_CIPHER_MODE_CFB, 0) != 0)
+ {
+ rc = -4;
+ goto decend;
+ }
+ if (gcry_cipher_setkey (*hd_cipher, key, length_key) != 0)
+ {
+ rc = -5;
+ goto decend;
+ }
+ if (gcry_cipher_decrypt (*hd_cipher,
+ decrypted_hash_data, length_buffer - SALT_SIZE,
+ buffer + SALT_SIZE, length_buffer - SALT_SIZE) != 0)
+ {
+ rc = -6;
+ goto decend;
+ }
+
+ /* check if hash is OK for decrypted data */
+ if (gcry_md_open (hd_md, hash_algo, 0) != 0)
+ {
+ rc = -7;
+ goto decend;
+ }
+ gcry_md_write (*hd_md, decrypted_hash_data + length_hash,
+ length_buffer - SALT_SIZE - length_hash);
+ ptr_hash = gcry_md_read (*hd_md, hash_algo);
+ if (!ptr_hash)
+ {
+ rc = -7;
+ goto decend;
+ }
+ if (memcmp (ptr_hash, decrypted_hash_data, length_hash) != 0)
+ {
+ rc = -8;
+ goto decend;
+ }
+
+ /* return the decrypted data */
+ *length_decrypted = length_buffer - SALT_SIZE - length_hash;
+ *decrypted = malloc (*length_decrypted);
+ if (!*decrypted)
+ goto decend;
+
+ memcpy (*decrypted, decrypted_hash_data + length_hash, *length_decrypted);
+
+ rc = 0;
+
+decend:
+ if (hd_md)
+ {
+ gcry_md_close (*hd_md);
+ free (hd_md);
+ }
+ if (hd_cipher)
+ {
+ gcry_cipher_close (*hd_cipher);
+ free (hd_cipher);
+ }
+ if (key)
+ free (key);
+ if (decrypted_hash_data)
+ free (decrypted_hash_data);
+
+ return rc;
+}
+
+/*
+ * Decrypts data still encrypted (data that could not be decrypted when reading
+ * secured data configuration file (because no passphrase was given).
+ *
+ * Returns:
+ * > 0: number of decrypted data
+ * 0: error decrypting data
+ */
+
+int
+secure_decrypt_data_not_decrypted (const char *passphrase)
+{
+ char **keys, *buffer, *decrypted;
+ const char *value;
+ int num_ok, num_keys, i, length_buffer, length_decrypted, rc;
+
+ /* we need a passphrase to decrypt data! */
+ if (!passphrase || !passphrase[0])
+ return 0;
+
+ num_ok = 0;
+
+ keys = string_split (hashtable_get_string (secure_hashtable_data_encrypted,
+ "keys"),
+ ",", 0, 0, &num_keys);
+ if (keys)
+ {
+ for (i = 0; i < num_keys; i++)
+ {
+ value = hashtable_get (secure_hashtable_data_encrypted, keys[i]);
+ if (value && value[0])
+ {
+ buffer = malloc (strlen (value) + 1);
+ if (buffer)
+ {
+ length_buffer = string_decode_base16 (value, buffer);
+ decrypted = NULL;
+ length_decrypted = 0;
+ rc = secure_decrypt_data (buffer,
+ length_buffer,
+ secure_hash_algo[CONFIG_INTEGER(secure_config_crypt_hash_algo)],
+ secure_cipher[CONFIG_INTEGER(secure_config_crypt_cipher)],
+ passphrase,
+ &decrypted,
+ &length_decrypted);
+ if ((rc == 0) && decrypted)
+ {
+ hashtable_set (secure_hashtable_data, keys[i],
+ decrypted);
+ hashtable_remove (secure_hashtable_data_encrypted,
+ keys[i]);
+ num_ok++;
+ }
+ if (decrypted)
+ free (decrypted);
+ free (buffer);
+ }
+ }
+ }
+ string_free_split (keys);
+ }
+
+ return num_ok;
+}
+
+/*
+ * Gets passphrase from user and puts it in variable "secure_passphrase".
+ */
+
+void
+secure_get_passphrase_from_user (const char *error)
+{
+ char passphrase[1024];
+
+ while (1)
+ {
+ gui_main_get_password (_("Please enter your passphrase to decrypt the "
+ "data secured by WeeChat:"),
+ _("(enter just one space to skip the passphrase, "
+ "but this will DISABLE all secured data!)"),
+ error,
+ passphrase, sizeof (passphrase));
+ if (secure_passphrase)
+ {
+ free (secure_passphrase);
+ secure_passphrase = NULL;
+ }
+ if (passphrase[0])
+ {
+ /* the special value " " (one space) disables passphrase */
+ if (strcmp (passphrase, " ") == 0)
+ {
+ gui_chat_printf (NULL,
+ _("To recover your secured data, you can issue "
+ "commands: \"/secure passphrase xxx\" and "
+ "\"/reload sec\""));
+ }
+ else
+ secure_passphrase = strdup (passphrase);
+ return;
+ }
+ }
+}
+
+/*
+ * Gets passphrase from a file.
+ *
+ * Returns passphrase read in file (only the first line with max length of
+ * 1024 chars), or NULL if error.
+ */
+
+char *
+secure_get_passphrase_from_file (const char *filename)
+{
+ FILE *file;
+ char *passphrase, *filename2, buffer[1024+1], *pos;
+ size_t num_read;
+
+ passphrase = NULL;
+
+ filename2 = string_expand_home (filename);
+ if (!filename2)
+ return NULL;
+
+ file = fopen (filename2, "r");
+ if (file)
+ {
+ num_read = fread (buffer, 1, sizeof (buffer) - 1, file);
+ if (num_read > 0)
+ {
+ buffer[num_read] = '\0';
+ pos = strchr (buffer, '\r');
+ if (pos)
+ pos[0] = '\0';
+ pos = strchr (buffer, '\n');
+ if (pos)
+ pos[0] = '\0';
+ if (buffer[0])
+ passphrase = strdup (buffer);
+ }
+ fclose (file);
+ }
+
+ free (filename2);
+
+ return passphrase;
+}
+
+/*
+ * Checks option "sec.crypt.passphrase_file".
+ */
+
+int
+secure_check_crypt_passphrase_file (void *data,
+ struct t_config_option *option,
+ const char *value)
+{
+ char *passphrase;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) option;
+
+ /* empty value is OK in option (no file used for passphrase) */
+ if (!value || !value[0])
+ return 1;
+
+ passphrase = secure_get_passphrase_from_file (value);
+ if (passphrase)
+ free (passphrase);
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%sWarning: unable to read passphrase from file "
+ "\"%s\""),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ value);
+ }
+
+ return 1;
+}
+
+/*
+ * Reloads secured data configuration file.
+ *
+ * Returns:
+ * WEECHAT_CONFIG_READ_OK: OK
+ * WEECHAT_CONFIG_READ_MEMORY_ERROR: not enough memory
+ * WEECHAT_CONFIG_READ_FILE_NOT_FOUND: file not found
+ */
+
+int
+secure_reload_cb (void *data, struct t_config_file *config_file)
+{
+ /* make C compiler happy */
+ (void) data;
+
+ if (secure_hashtable_data_encrypted->items_count > 0)
+ {
+ gui_chat_printf (NULL,
+ _("%sError: not possible to reload file sec.conf "
+ "because there is still encrypted data (use /secure "
+ "decrypt, see /help secure)"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]);
+ return WEECHAT_CONFIG_READ_FILE_NOT_FOUND;
+ }
+
+ secure_data_encrypted = 0;
+
+ /* remove all secured data */
+ hashtable_remove_all (secure_hashtable_data);
+
+ return config_file_reload (config_file);
+}
+
+/*
+ * Reads a data option in secured data configuration file.
+ */
+
+int
+secure_data_read_cb (void *data,
+ struct t_config_file *config_file,
+ struct t_config_section *section,
+ const char *option_name, const char *value)
+{
+ char *buffer, *decrypted, str_error[1024];
+ int length_buffer, length_decrypted, rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) config_file;
+ (void) section;
+
+ if (!option_name || !value || !value[0])
+ {
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+ }
+
+ /* special line indicating if a passphrase must be used to decrypt data */
+ if (strcmp (option_name, SECURE_DATA_PASSPHRASE_FLAG) == 0)
+ {
+ secure_data_encrypted = config_file_string_to_boolean (value);
+ if (secure_data_encrypted && !secure_passphrase && !gui_init_ok)
+ {
+ /* if a passphrase file is set, use it */
+ if (CONFIG_STRING(secure_config_crypt_passphrase_file)[0])
+ secure_passphrase = secure_get_passphrase_from_file (CONFIG_STRING(secure_config_crypt_passphrase_file));
+
+ /* ask passphrase to the user (if no file, or file not found) */
+ if (!secure_passphrase)
+ secure_get_passphrase_from_user ("");
+ }
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+ }
+
+ if (!secure_data_encrypted)
+ {
+ /* clear data: just store value in hashtable */
+ hashtable_set (secure_hashtable_data, option_name, value);
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+ }
+
+ /* check that passphrase is set */
+ if (!secure_passphrase)
+ {
+ gui_chat_printf (NULL,
+ _("%sPassphrase is not set, unable to decrypt data \"%s\""),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ option_name);
+ hashtable_set (secure_hashtable_data_encrypted, option_name, value);
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+ }
+
+ /* decrypt data */
+ buffer = malloc (strlen (value) + 1);
+ if (!buffer)
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+
+ length_buffer = string_decode_base16 (value, buffer);
+ while (1)
+ {
+ decrypted = NULL;
+ length_decrypted = 0;
+ rc = secure_decrypt_data (buffer,
+ length_buffer,
+ secure_hash_algo[CONFIG_INTEGER(secure_config_crypt_hash_algo)],
+ secure_cipher[CONFIG_INTEGER(secure_config_crypt_cipher)],
+ secure_passphrase,
+ &decrypted,
+ &length_decrypted);
+ if (rc == 0)
+ {
+ if (decrypted)
+ {
+ hashtable_set (secure_hashtable_data, option_name,
+ decrypted);
+ free (decrypted);
+ break;
+ }
+ }
+ else
+ {
+ if (decrypted)
+ free (decrypted);
+ if (gui_init_ok)
+ {
+ gui_chat_printf (NULL,
+ _("%sWrong passphrase, unable to decrypt data "
+ "\"%s\""),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ option_name);
+ break;
+ }
+ snprintf (str_error, sizeof (str_error),
+ _("*** Wrong passphrase (decrypt error: %s) ***"),
+ secure_decrypt_error[(rc * -1) - 1]);
+ secure_get_passphrase_from_user (str_error);
+ if (!secure_passphrase)
+ {
+ gui_chat_printf (NULL,
+ _("%sPassphrase is not set, unable to decrypt "
+ "data \"%s\""),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ option_name);
+ hashtable_set (secure_hashtable_data_encrypted, option_name,
+ value);
+ break;
+ }
+ }
+ }
+ free (buffer);
+
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+}
+
+/*
+ * Encrypts data and writes it in secured data configuration file.
+ */
+
+void
+secure_data_write_map_cb (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ struct t_config_file *config_file;
+ char *buffer, *buffer_base16;
+ int length_buffer, rc;
+
+ /* make C compiler happy */
+ (void) hashtable;
+
+ config_file = (struct t_config_file *)data;
+
+ buffer = NULL;
+ length_buffer = 0;
+
+ if (secure_passphrase)
+ {
+ /* encrypt password using passphrase */
+ rc = secure_encrypt_data (value, strlen (value) + 1,
+ secure_hash_algo[CONFIG_INTEGER(secure_config_crypt_hash_algo)],
+ secure_cipher[CONFIG_INTEGER(secure_config_crypt_cipher)],
+ secure_passphrase,
+ &buffer,
+ &length_buffer);
+ if (rc == 0)
+ {
+ if (buffer)
+ {
+ buffer_base16 = malloc ((length_buffer * 2) + 1);
+ if (buffer_base16)
+ {
+ string_encode_base16 (buffer, length_buffer, buffer_base16);
+ config_file_write_line (config_file, key,
+ "\"%s\"", buffer_base16);
+ free (buffer_base16);
+ }
+ free (buffer);
+ }
+ }
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%sError encrypting data \"%s\" (%d)"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ key, rc);
+ }
+ }
+ else
+ {
+ /* store password as plain text */
+ config_file_write_line (config_file, key, "\"%s\"", value);
+ }
+}
+
+/*
+ * Writes already encrypted data in secured data configuration file.
+ */
+
+void
+secure_data_write_map_encrypted_cb (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ struct t_config_file *config_file;
+
+ /* make C compiler happy */
+ (void) hashtable;
+
+ config_file = (struct t_config_file *)data;
+
+ /* store data as-is (it is already encrypted) */
+ config_file_write_line (config_file, key, "\"%s\"", value);
+}
+
+/*
+ * Writes section "data" in secured data configuration file.
+ */
+
+int
+secure_data_write_cb (void *data, struct t_config_file *config_file,
+ const char *section_name)
+{
+ /* make C compiler happy */
+ (void) data;
+
+ /* write name of section */
+ if (!config_file_write_line (config_file, section_name, NULL))
+ return WEECHAT_CONFIG_WRITE_ERROR;
+
+ if (secure_hashtable_data->items_count > 0)
+ {
+ /*
+ * write a special line indicating if a passphrase must be used to
+ * decrypt data (if not, then data is stored as plain text)
+ */
+ if (!config_file_write_line (config_file,
+ SECURE_DATA_PASSPHRASE_FLAG,
+ (secure_passphrase) ? "on" : "off"))
+ {
+ return WEECHAT_CONFIG_WRITE_ERROR;
+ }
+ /* encrypt and write secured data */
+ hashtable_map (secure_hashtable_data,
+ &secure_data_write_map_cb, config_file);
+ }
+ else if (secure_hashtable_data_encrypted->items_count > 0)
+ {
+ /*
+ * if there is encrypted data, that means passphrase was not set and
+ * we were unable to decrypt => just save the encrypted content
+ * as-is (so that content of sec.conf is not lost)
+ */
+ if (!config_file_write_line (config_file,
+ SECURE_DATA_PASSPHRASE_FLAG, "on"))
+ {
+ return WEECHAT_CONFIG_WRITE_ERROR;
+ }
+ hashtable_map (secure_hashtable_data_encrypted,
+ &secure_data_write_map_encrypted_cb, config_file);
+ }
+
+ return WEECHAT_CONFIG_WRITE_OK;
+}
+
+/*
+ * Creates options in secured data configuration.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+secure_init_options ()
+{
+ struct t_config_section *ptr_section;
+
+ secure_config_file = config_file_new (NULL, SECURE_CONFIG_NAME,
+ &secure_reload_cb, NULL);
+ if (!secure_config_file)
+ return 0;
+
+ /* crypt */
+ ptr_section = config_file_new_section (secure_config_file, "crypt",
+ 0, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ if (!ptr_section)
+ {
+ config_file_free (secure_config_file);
+ return 0;
+ }
+
+ secure_config_crypt_cipher = config_file_new_option (
+ secure_config_file, ptr_section,
+ "cipher", "integer",
+ N_("cipher used to crypt data (the number after algorithm is the size "
+ "of the key in bits)"),
+ "aes128|aes192|aes256", 0, 0, "aes256", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ secure_config_crypt_hash_algo = config_file_new_option (
+ secure_config_file, ptr_section,
+ "hash_algo", "integer",
+ N_("hash algorithm used to check the decrypted data"),
+ "sha224|sha256|sha384|sha512", 0, 0, "sha256", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ secure_config_crypt_passphrase_file = config_file_new_option (
+ secure_config_file, ptr_section,
+ "passphrase_file", "string",
+ N_("path to a file containing the passphrase to encrypt/decrypt secured "
+ "data (used only when reading file sec.conf); only first line of "
+ "file is used; this file is used only if the environment variable \""
+ SECURE_ENV_PASSPHRASE "\" is not set (the environment variable has "
+ "higher priority); security note: it is recommended to keep this "
+ "file readable only by you and store it outside WeeChat home (for "
+ "example in your home); example: \"~/.weechat-passphrase\""),
+ NULL, 0, 0, "", NULL, 0,
+ &secure_check_crypt_passphrase_file, NULL, NULL, NULL, NULL, NULL);
+ secure_config_crypt_salt = config_file_new_option (
+ secure_config_file, ptr_section,
+ "salt", "boolean",
+ N_("use salt when generating key used in encryption (recommended for "
+ "maximum security); when enabled, the content of crypted data in "
+ "file sec.conf will be different on each write of the file; if you "
+ "put the file sec.conf in a version control system, then you "
+ "can turn off this option to have always same content in file"),
+ NULL, 0, 0, "on", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ /* data */
+ ptr_section = config_file_new_section (secure_config_file, "data",
+ 0, 0,
+ &secure_data_read_cb, NULL,
+ &secure_data_write_cb, NULL,
+ &secure_data_write_cb, NULL,
+ NULL, NULL, NULL, NULL);
+ if (!ptr_section)
+ {
+ config_file_free (secure_config_file);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Initializes secured data configuration.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+secure_init ()
+{
+ int rc;
+ char *ptr_phrase;
+
+ /* try to read passphrase (if not set) from env var "WEECHAT_PASSPHRASE" */
+ if (!secure_passphrase)
+ {
+ ptr_phrase = getenv (SECURE_ENV_PASSPHRASE);
+ if (ptr_phrase)
+ {
+ if (ptr_phrase[0])
+ secure_passphrase = strdup (ptr_phrase);
+ unsetenv (SECURE_ENV_PASSPHRASE);
+ }
+ }
+
+ secure_hashtable_data = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!secure_hashtable_data)
+ return 0;
+
+ secure_hashtable_data_encrypted = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!secure_hashtable_data_encrypted)
+ {
+ hashtable_free (secure_hashtable_data);
+ return 0;
+ }
+
+ rc = secure_init_options ();
+
+ if (!rc)
+ {
+ gui_chat_printf (NULL,
+ _("FATAL: error initializing configuration options"));
+ }
+
+ return rc;
+}
+
+/*
+ * Reads secured data configuration file.
+ *
+ * Returns:
+ * WEECHAT_CONFIG_READ_OK: OK
+ * WEECHAT_CONFIG_READ_MEMORY_ERROR: not enough memory
+ * WEECHAT_CONFIG_READ_FILE_NOT_FOUND: file not found
+ */
+
+int
+secure_read ()
+{
+ int rc;
+
+ secure_data_encrypted = 0;
+
+ rc = config_file_read (secure_config_file);
+
+ if (rc != WEECHAT_CONFIG_READ_OK)
+ {
+ gui_chat_printf (NULL,
+ _("%sError reading configuration"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]);
+ }
+
+ return rc;
+}
+
+/*
+ * Writes secured data configuration file.
+ *
+ * Returns:
+ * WEECHAT_CONFIG_WRITE_OK: OK
+ * WEECHAT_CONFIG_WRITE_ERROR: error
+ * WEECHAT_CONFIG_WRITE_MEMORY_ERROR: not enough memory
+ */
+
+int
+secure_write ()
+{
+ return config_file_write (secure_config_file);
+}
+
+/*
+ * Frees secured data file and variables.
+ */
+
+void
+secure_free ()
+{
+ config_file_free (secure_config_file);
+
+ if (secure_hashtable_data)
+ {
+ hashtable_free (secure_hashtable_data);
+ secure_hashtable_data = NULL;
+ }
+ if (secure_hashtable_data_encrypted)
+ {
+ hashtable_free (secure_hashtable_data_encrypted);
+ secure_hashtable_data_encrypted = NULL;
+ }
+}
+
+/*
+ * Displays a secured data.
+ */
+
+void
+secure_buffer_display_data (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ int *line;
+
+ /* make C compiler happy */
+ (void) value;
+
+ line = (int *)data;
+
+ if (secure_buffer_display_values && (hashtable == secure_hashtable_data))
+ {
+ gui_chat_printf_y (secure_buffer, (*line)++,
+ " %s%s = %s\"%s%s%s\"",
+ key,
+ GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS),
+ GUI_COLOR(GUI_COLOR_CHAT),
+ GUI_COLOR(GUI_COLOR_CHAT_VALUE),
+ value,
+ GUI_COLOR(GUI_COLOR_CHAT));
+ }
+ else
+ {
+ gui_chat_printf_y (secure_buffer, (*line)++,
+ " %s", key);
+ }
+}
+
+/*
+ * Displays content of secured data buffer.
+ */
+
+void
+secure_buffer_display ()
+{
+ int line, count, count_encrypted;
+
+ if (!secure_buffer)
+ return;
+
+ gui_buffer_clear (secure_buffer);
+
+ /* set title buffer */
+ gui_buffer_set_title (secure_buffer,
+ _("WeeChat secured data (sec.conf) | "
+ "Keys: [alt-v] Toggle values"));
+
+ line = 0;
+
+ gui_chat_printf_y (secure_buffer, line++,
+ "Hash algo: %s Cipher: %s Salt: %s",
+ secure_hash_algo_string[CONFIG_INTEGER(secure_config_crypt_hash_algo)],
+ secure_cipher_string[CONFIG_INTEGER(secure_config_crypt_cipher)],
+ (CONFIG_BOOLEAN(secure_config_crypt_salt)) ? _("on") : _("off"));
+
+ /* display passphrase */
+ line++;
+ if (secure_passphrase)
+ {
+ if (secure_buffer_display_values)
+ {
+ gui_chat_printf_y (secure_buffer, line++,
+ "%s%s = %s\"%s%s%s\"",
+ _("Passphrase"),
+ GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS),
+ GUI_COLOR(GUI_COLOR_CHAT),
+ GUI_COLOR(GUI_COLOR_CHAT_VALUE),
+ secure_passphrase,
+ GUI_COLOR(GUI_COLOR_CHAT));
+ }
+ else
+ gui_chat_printf_y (secure_buffer, line++, _("Passphrase is set"));
+ }
+ else
+ {
+ gui_chat_printf_y (secure_buffer, line++,
+ _("Passphrase is NOT set"));
+ }
+
+ /* display secured data */
+ count = secure_hashtable_data->items_count;
+ count_encrypted = secure_hashtable_data_encrypted->items_count;
+ if (count > 0)
+ {
+ line++;
+ gui_chat_printf_y (secure_buffer, line++, _("Secured data:"));
+ line++;
+ hashtable_map (secure_hashtable_data,
+ &secure_buffer_display_data, &line);
+ }
+ /* display secured data not decrypted */
+ if (count_encrypted > 0)
+ {
+ line++;
+ gui_chat_printf_y (secure_buffer, line++,
+ _("Secured data STILL ENCRYPTED: (use /secure decrypt, "
+ "see /help secure)"));
+ line++;
+ hashtable_map (secure_hashtable_data_encrypted,
+ &secure_buffer_display_data, &line);
+ }
+ if ((count == 0) && (count_encrypted == 0))
+ {
+ line++;
+ gui_chat_printf_y (secure_buffer, line++, _("No secured data set"));
+ }
+}
+
+/*
+ * Input callback for secured data buffer.
+ */
+
+int
+secure_buffer_input_cb (void *data, struct t_gui_buffer *buffer,
+ const char *input_data)
+{
+ /* make C compiler happy */
+ (void) data;
+
+ if (string_strcasecmp (input_data, "q") == 0)
+ {
+ gui_buffer_close (buffer);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Close callback for secured data buffer.
+ */
+
+int
+secure_buffer_close_cb (void *data, struct t_gui_buffer *buffer)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) buffer;
+
+ secure_buffer = NULL;
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Assigns secured data buffer to pointer if it is not yet set.
+ */
+
+void
+secure_buffer_assign ()
+{
+ if (!secure_buffer)
+ {
+ secure_buffer = gui_buffer_search_by_name (NULL, SECURE_BUFFER_NAME);
+ if (secure_buffer)
+ {
+ secure_buffer->input_callback = &secure_buffer_input_cb;
+ secure_buffer->close_callback = &secure_buffer_close_cb;
+ }
+ }
+}
+
+/*
+ * Opens a buffer to display secured data.
+ */
+
+void
+secure_buffer_open ()
+{
+ if (!secure_buffer)
+ {
+ secure_buffer = gui_buffer_new (NULL, SECURE_BUFFER_NAME,
+ &secure_buffer_input_cb, NULL,
+ &secure_buffer_close_cb, NULL);
+ if (secure_buffer)
+ {
+ if (!secure_buffer->short_name)
+ secure_buffer->short_name = strdup (SECURE_BUFFER_NAME);
+ gui_buffer_set (secure_buffer, "type", "free");
+ gui_buffer_set (secure_buffer, "localvar_set_no_log", "1");
+ gui_buffer_set (secure_buffer, "key_bind_meta-v", "/secure toggle_values");
+ }
+ secure_buffer_display_values = 0;
+ }
+
+ if (!secure_buffer)
+ return;
+
+ gui_window_switch_to_buffer (gui_current_window, secure_buffer, 1);
+
+ secure_buffer_display ();
+}
diff --git a/src/core/wee-secure.h b/src/core/wee-secure.h
new file mode 100644
index 000000000..9db72fcd4
--- /dev/null
+++ b/src/core/wee-secure.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 Sebastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat 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.
+ *
+ * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_SECURE_H
+#define __WEECHAT_SECURE_H 1
+
+#define SECURE_CONFIG_NAME "sec"
+
+#define SECURE_ENV_PASSPHRASE "WEECHAT_PASSPHRASE"
+#define SECURE_SALT_DEFAULT "WeeChat!"
+#define SECURE_DATA_PASSPHRASE_FLAG "__passphrase__"
+
+#define SECURE_BUFFER_NAME "secured_data"
+
+enum t_secure_config_hash_algo
+{
+ SECURE_CONFIG_HASH_SHA224 = 0,
+ SECURE_CONFIG_HASH_SHA256,
+ SECURE_CONFIG_HASH_SHA384,
+ SECURE_CONFIG_HASH_SHA512,
+};
+
+enum t_secure_config_cipher
+{
+ SECURE_CONFIG_CIPHER_AES128 = 0,
+ SECURE_CONFIG_CIPHER_AES192,
+ SECURE_CONFIG_CIPHER_AES256,
+};
+
+extern struct t_config_file *secure_config_file;
+extern struct t_config_section *secure_config_section_pwd;
+
+extern struct t_config_option *secure_config_crypt_cipher;
+extern struct t_config_option *secure_config_crypt_hash_algo;
+extern struct t_config_option *secure_config_crypt_passphrase_file;
+extern struct t_config_option *secure_config_crypt_salt;
+
+extern char *secure_passphrase;
+extern struct t_hashtable *secure_hashtable_data;
+extern struct t_hashtable *secure_hashtable_data_encrypted;
+
+extern struct t_gui_buffer *secure_buffer;
+extern int secure_buffer_display_values;
+
+extern int secure_decrypt_data_not_decrypted (const char *passphrase);
+extern int secure_init ();
+extern int secure_read ();
+extern int secure_write ();
+extern void secure_free ();
+extern void secure_buffer_display ();
+extern void secure_buffer_assign ();
+extern void secure_buffer_open ();
+
+#endif /* __WEECHAT_SECURE_H */
diff --git a/src/core/wee-string.c b/src/core/wee-string.c
index 94402b52c..34ad3b1bb 100644
--- a/src/core/wee-string.c
+++ b/src/core/wee-string.c
@@ -1797,6 +1797,78 @@ string_format_size (unsigned long long size)
}
/*
+ * Encodes a string in base16 (hexadecimal).
+ *
+ * Argument "length" is number of bytes in "from" to convert (commonly
+ * strlen(from)).
+ */
+
+void
+string_encode_base16 (const char *from, int length, char *to)
+{
+ int i;
+ const char *hexa = "0123456789ABCDEF";
+ char *ptr_to;
+
+ ptr_to = to;
+ ptr_to[0] = '\0';
+ for (i = 0; i < length; i++)
+ {
+ ptr_to[0] = hexa[((unsigned char)from[i]) / 16];
+ ptr_to[1] = hexa[((unsigned char)from[i]) % 16];
+ ptr_to += 2;
+ }
+ ptr_to[0] = '\0';
+}
+
+/*
+ * Decodes a base16 string (hexadecimal).
+ *
+ * Returns length of string in "*to" (it does not count final \0).
+ */
+
+int
+string_decode_base16 (const char *from, char *to)
+{
+ int length, to_length, i, pos;
+ unsigned char *ptr_to, value;
+
+ length = strlen (from) / 2;
+
+ ptr_to = (unsigned char *)to;
+ ptr_to[0] = '\0';
+ to_length = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ pos = i * 2;
+ value = 0;
+ /* 4 bits on the left */
+ if ((from[pos] >= '0') && (from[pos] <= '9'))
+ value |= (from[pos] - '0') << 4;
+ else if ((from[pos] >= 'a') && (from[pos] <= 'f'))
+ value |= (from[pos] - 'a' + 10) << 4;
+ else if ((from[pos] >= 'A') && (from[pos] <= 'F'))
+ value |= (from[pos] - 'A' + 10) << 4;
+ /* 4 bits on the right */
+ pos++;
+ if ((from[pos] >= '0') && (from[pos] <= '9'))
+ value |= from[pos] - '0';
+ else if ((from[pos] >= 'a') && (from[pos] <= 'f'))
+ value |= from[pos] - 'a' + 10;
+ else if ((from[pos] >= 'A') && (from[pos] <= 'F'))
+ value |= from[pos] - 'A' + 10;
+
+ ptr_to[0] = value;
+ ptr_to++;
+ to_length++;
+ }
+ ptr_to[0] = '\0';
+
+ return to_length;
+}
+
+/*
* Converts 3 bytes of 8 bits in 4 bytes of 6 bits.
*/
@@ -2031,19 +2103,25 @@ string_input_for_buffer (const char *string)
char *
string_replace_with_callback (const char *string,
+ const char *prefix,
+ const char *suffix,
char *(*callback)(void *data, const char *text),
void *callback_data,
int *errors)
{
- int length, length_value, index_string, index_result;
+ int length_prefix, length_suffix, length, length_value, index_string;
+ int index_result;
char *result, *result2, *key, *value;
const char *pos_end_name;
*errors = 0;
- if (!string)
+ if (!string || !prefix || !prefix[0] || !suffix || !suffix[0])
return NULL;
+ length_prefix = strlen (prefix);
+ length_suffix = strlen (suffix);
+
length = strlen (string) + 1;
result = malloc (length);
if (result)
@@ -2053,19 +2131,18 @@ string_replace_with_callback (const char *string,
while (string[index_string])
{
if ((string[index_string] == '\\')
- && (string[index_string + 1] == '$'))
+ && (string[index_string + 1] == prefix[0]))
{
index_string++;
result[index_result++] = string[index_string++];
}
- else if ((string[index_string] == '$')
- && (string[index_string + 1] == '{'))
+ else if (strncmp (string + index_string, prefix, length_prefix) == 0)
{
- pos_end_name = strchr (string + index_string + 2, '}');
+ pos_end_name = strstr (string + index_string + length_prefix, suffix);
if (pos_end_name)
{
- key = string_strndup (string + index_string + 2,
- pos_end_name - (string + index_string + 2));
+ key = string_strndup (string + index_string + length_prefix,
+ pos_end_name - (string + index_string + length_prefix));
if (key)
{
value = (*callback) (callback_data, key);
@@ -2086,7 +2163,7 @@ string_replace_with_callback (const char *string,
strcpy (result + index_result, value);
index_result += length_value;
index_string += pos_end_name - string -
- index_string + 1;
+ index_string + length_suffix;
free (value);
}
else
@@ -2094,7 +2171,6 @@ string_replace_with_callback (const char *string,
result[index_result++] = string[index_string++];
(*errors)++;
}
-
free (key);
}
else
diff --git a/src/core/wee-string.h b/src/core/wee-string.h
index 0e6e50568..f79795707 100644
--- a/src/core/wee-string.h
+++ b/src/core/wee-string.h
@@ -72,11 +72,15 @@ extern char *string_iconv_from_internal (const char *charset,
const char *string);
extern int string_iconv_fprintf (FILE *file, const char *data, ...);
extern char *string_format_size (unsigned long long size);
+extern void string_encode_base16 (const char *from, int length, char *to);
+extern int string_decode_base16 (const char *from, char *to);
extern void string_encode_base64 (const char *from, int length, char *to);
extern int string_decode_base64 (const char *from, char *to);
extern int string_is_command_char (const char *string);
extern const char *string_input_for_buffer (const char *string);
extern char *string_replace_with_callback (const char *string,
+ const char *prefix,
+ const char *suffix,
char *(*callback)(void *data, const char *text),
void *callback_data,
int *errors);
diff --git a/src/core/wee-upgrade.c b/src/core/wee-upgrade.c
index 44f19f12c..09af3d0b2 100644
--- a/src/core/wee-upgrade.c
+++ b/src/core/wee-upgrade.c
@@ -32,6 +32,7 @@
#include "wee-upgrade.h"
#include "wee-hook.h"
#include "wee-infolist.h"
+#include "wee-secure.h"
#include "wee-string.h"
#include "wee-util.h"
#include "../gui/gui-buffer.h"
@@ -729,6 +730,9 @@ upgrade_weechat_load ()
gui_color_buffer_assign ();
gui_color_buffer_display ();
+ secure_buffer_assign ();
+ secure_buffer_display ();
+
if (upgrade_layout->layout_buffers)
gui_layout_buffer_apply (upgrade_layout);
if (upgrade_layout->layout_windows)
diff --git a/src/core/weechat.c b/src/core/weechat.c
index 984477cc7..750493b95 100644
--- a/src/core/weechat.c
+++ b/src/core/weechat.c
@@ -61,6 +61,7 @@
#include "wee-log.h"
#include "wee-network.h"
#include "wee-proxy.h"
+#include "wee-secure.h"
#include "wee-string.h"
#include "wee-upgrade.h"
#include "wee-utf8.h"
@@ -440,14 +441,19 @@ main (int argc, char *argv[])
command_init (); /* initialize WeeChat commands */
completion_init (); /* add core completion hooks */
gui_key_init (); /* init keys */
- if (!config_weechat_init ()) /* init options with default values */
+ network_init_gcrypt (); /* init gcrypt */
+ if (!secure_init ()) /* init secured data options (sec.*)*/
+ exit (EXIT_FAILURE);
+ if (!config_weechat_init ()) /* init WeeChat options (weechat.*) */
exit (EXIT_FAILURE);
weechat_parse_args (argc, argv); /* parse command line args */
weechat_create_home_dir (); /* create WeeChat home directory */
log_init (); /* init log file */
- if (config_weechat_read () < 0) /* read WeeChat configuration */
+ if (secure_read () < 0) /* read secured data options */
+ exit (EXIT_FAILURE);
+ if (config_weechat_read () < 0) /* read WeeChat options */
exit (EXIT_FAILURE);
- network_init (); /* init networking */
+ network_init_gnutls (); /* init GnuTLS */
gui_main_init (); /* init WeeChat interface */
if (weechat_upgrading)
{
@@ -470,10 +476,12 @@ main (int argc, char *argv[])
gui_layout_save_on_exit (); /* save layout */
plugin_end (); /* end plugin interface(s) */
if (CONFIG_BOOLEAN(config_look_save_config_on_exit))
- (void) config_weechat_write (NULL); /* save WeeChat config file */
+ (void) config_weechat_write (); /* save WeeChat config file */
+ (void) secure_write (); /* save secured data */
gui_main_end (1); /* shut down WeeChat GUI */
proxy_free_all (); /* free all proxies */
- config_weechat_free (); /* free weechat.conf and vars */
+ config_weechat_free (); /* free WeeChat options */
+ secure_free (); /* free secured data options */
config_file_free_all (); /* free all configuration files */
gui_key_end (); /* remove all keys */
unhook_all (); /* remove all hooks */