/* * wee-secure.c - secured data configuration options (file sec.conf) * * Copyright (C) 2013-2015 Sébastien Helleu * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #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 (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; 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); 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); 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 (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 (); } /* * 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; } }