1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-26 04:46:37 +02:00
Files
weechat/src/core/wee-secure.c
T
Sébastien Helleu cf6aca1619 core: add pointer in some callbacks (closes #406)
This pointer is the first argument received by callbacks, and the
existing argument "data" is now automatically freed by WeeChat when the
object containing the callback is removed.

With this new pointer, the linked list of callbacks in scripts has been
removed. This will improve speed of scripts (using a lot of hooks),
reduce memory used by scripts and reduce time to unload scripts.

Following functions are affected in the C API:

* exec_on_files
* config_new
* config_new_section
* config_new_option
* hook_command
* hook_command_run
* hook_timer
* hook_fd
* hook_process
* hook_process_hashtable
* hook_connect
* hook_print
* hook_signal
* hook_hsignal
* hook_config
* hook_completion
* hook_modifier
* hook_info
* hook_info_hashtable
* hook_infolist
* hook_hdata
* hook_focus
* unhook_all_plugin
* buffer_new
* bar_item_new
* upgrade_new
* upgrade_read
2016-03-21 18:11:21 +01:00

1385 lines
39 KiB
C

/*
* wee-secure.c - secured data configuration options (file sec.conf)
*
* Copyright (C) 2013-2016 Sébastien 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;
}
/*
* Derives a key from salt + passphrase (using a hash).
*
* Returns:
* 1: OK
* 0: error
*/
int
secure_derive_key (const char *salt, const char *passphrase,
unsigned char *key, int length_key)
{
unsigned char *buffer, *ptr_hash;
int length, length_hash;
gcry_md_hd_t hd_md;
memset (key, 0, length_key);
length = SALT_SIZE + strlen (passphrase);
buffer = malloc (length);
if (!buffer)
return 0;
/* build a buffer with salt + passphrase */
memcpy (buffer, salt, SALT_SIZE);
memcpy (buffer + SALT_SIZE, passphrase, strlen (passphrase));
/* compute hash of buffer */
if (gcry_md_open (&hd_md, GCRY_MD_SHA512, 0) != 0)
{
free (buffer);
return 0;
}
length_hash = gcry_md_get_algo_dlen (GCRY_MD_SHA512);
gcry_md_write (hd_md, buffer, length);
ptr_hash = gcry_md_read (hd_md, GCRY_MD_SHA512);
if (!ptr_hash)
{
gcry_md_close (hd_md);
free (buffer);
return 0;
}
/* copy beginning of hash (or full hash) in the key */
memcpy (key, ptr_hash,
(length_hash > length_key) ? length_key : length_hash);
gcry_md_close (hd_md);
free (buffer);
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;
int hd_md_opened, hd_cipher_opened;
gcry_md_hd_t *hd_md;
gcry_cipher_hd_t *hd_cipher;
char salt[SALT_SIZE];
unsigned char *ptr_hash, *key, *hash_and_data;
rc = -1;
hd_md = NULL;
hd_md_opened = 0;
hd_cipher = NULL;
hd_cipher_opened = 0;
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 (!secure_derive_key (salt, passphrase, key, length_key))
{
rc = -2;
goto encend;
}
/* compute hash of data */
if (gcry_md_open (hd_md, hash_algo, 0) != 0)
{
rc = -3;
goto encend;
}
hd_md_opened = 1;
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;
}
hd_cipher_opened = 1;
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)
{
if (hd_md_opened)
gcry_md_close (*hd_md);
free (hd_md);
}
if (hd_cipher)
{
if (hd_cipher_opened)
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, hd_md_opened, hd_cipher_opened;
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_md_opened = 0;
hd_cipher = NULL;
hd_cipher_opened = 0;
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 (!secure_derive_key (buffer, passphrase, key, length_key))
{
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;
}
hd_cipher_opened = 1;
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;
}
hd_md_opened = 1;
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)
{
if (hd_md_opened)
gcry_md_close (*hd_md);
free (hd_md);
}
if (hd_cipher)
{
if (hd_cipher_opened)
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)
{
const char *prompt[5];
char passphrase[1024];
prompt[0] = _("Please enter your passphrase to decrypt the data secured "
"by WeeChat:");
prompt[1] = _("(enter just one space to skip the passphrase, but this "
"will DISABLE all secured data!)");
prompt[2] = _("(press ctrl-C to exit WeeChat now)");
prompt[3] = error;
prompt[4] = NULL;
while (1)
{
gui_main_get_password (prompt, 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 "
"use /secure decrypt (see /help secure)"));
}
else if (strcmp (passphrase, "\x03") == 0)
{
/* ctrl-C pressed, just exit now */
exit (1);
}
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.
*
* Note: result must be freed after use.
*/
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 (const void *pointer, void *data,
struct t_config_option *option,
const char *value)
{
char *passphrase;
/* make C compiler happy */
(void) pointer;
(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 (const void *pointer, void *data,
struct t_config_file *config_file)
{
/* make C compiler happy */
(void) pointer;
(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 (const void *pointer, 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) pointer;
(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 (const void *pointer, void *data,
struct t_config_file *config_file,
const char *section_name)
{
/* make C compiler happy */
(void) pointer;
(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, 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, 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, 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, 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; this option is used only when reading file sec.conf; only "
"first line of file is used; this file is used only if the "
"environment variable \"WEECHAT_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,
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, NULL, NULL, NULL);
/* data */
ptr_section = config_file_new_section (
secure_config_file, "data",
0, 0,
&secure_data_read_cb, NULL, NULL,
&secure_data_write_cb, NULL, NULL,
&secure_data_write_cb, NULL, NULL,
NULL, 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);
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++;
gui_chat_printf_y (secure_buffer, line++,
(secure_passphrase) ?
_("Passphrase is set") : _("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 (const void *pointer, void *data,
struct t_gui_buffer *buffer,
const char *input_data)
{
/* make C compiler happy */
(void) pointer;
(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 (const void *pointer, void *data,
struct t_gui_buffer *buffer)
{
/* make C compiler happy */
(void) pointer;
(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, NULL,
&secure_buffer_close_cb, NULL, 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 ();
}
/*
* Frees all allocated data.
*/
void
secure_end ()
{
if (secure_passphrase)
{
free (secure_passphrase);
secure_passphrase = NULL;
}
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;
}
}