summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-05-13 18:44:54 +0200
committerTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-05-13 18:44:54 +0200
commit399dbc615954efd5c80961f08bbaf294d95c6341 (patch)
treef1486474317870ff1d09d017573c8fa3ea5369fc
parent2e23cb092311086dd9acffc4630233cc9e943d19 (diff)
downloadpidgin-399dbc615954efd5c80961f08bbaf294d95c6341.tar.gz
Complete master password implementation
-rw-r--r--libpurple/account.c5
-rw-r--r--libpurple/plugins/keyrings/internalkeyring.c523
2 files changed, 491 insertions, 37 deletions
diff --git a/libpurple/account.c b/libpurple/account.c
index 53bc39fa11..2c90da9462 100644
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -1222,9 +1222,6 @@ request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields)
return;
}
- if (!remember)
- purple_keyring_set_password(account, NULL, NULL, NULL);
-
purple_account_set_remember_password(account, remember);
purple_account_set_password(account, entry, NULL, NULL);
@@ -2907,7 +2904,7 @@ connection_error_cb(PurpleConnection *gc,
static void
password_migration_cb(PurpleAccount *account)
{
- g_return_if_fail(account != NULL);
+ /* account may be NULL (means: all) */
schedule_accounts_save();
}
diff --git a/libpurple/plugins/keyrings/internalkeyring.c b/libpurple/plugins/keyrings/internalkeyring.c
index 74eb64bff0..e77432e500 100644
--- a/libpurple/plugins/keyrings/internalkeyring.c
+++ b/libpurple/plugins/keyrings/internalkeyring.c
@@ -35,8 +35,8 @@
#define INTKEYRING_NAME N_("Internal keyring")
#define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \
"storage behaviour for libpurple.")
-#define INTKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)"
-#define INTKEYRING_ID PURPLE_DEFAULT_KEYRING
+#define INTKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)"
+#define INTKEYRING_ID PURPLE_DEFAULT_KEYRING
#define INTKEYRING_VERIFY_STR "[verification-string]"
#define INTKEYRING_PBKDF2_ITERATIONS 10000
@@ -44,19 +44,66 @@
#define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000
#define INTKEYRING_KEY_LEN (256/8)
#define INTKEYRING_ENCRYPT_BUFF_LEN 1000
+#define INTKEYRING_ENCRYPTED_MIN_LEN 50
+#define INTKEYRING_ENCRYPTION_METHOD "pbkdf2-sha256-aes256"
#define INTKEYRING_PREFS "/plugins/keyrings/internal/"
typedef struct
{
+ enum
+ {
+ INTKEYRING_REQUEST_READ,
+ INTKEYRING_REQUEST_SAVE
+ } type;
+ PurpleAccount *account;
+ gchar *password;
+ union
+ {
+ PurpleKeyringReadCallback read;
+ PurpleKeyringSaveCallback save;
+ } cb;
+ gpointer cb_data;
+} intkeyring_request;
+
+typedef struct
+{
guchar *data;
size_t len;
} intkeyring_buff_t;
-static gboolean intkeyring_opened = FALSE;
+static intkeyring_buff_t *intkeyring_key;
static GHashTable *intkeyring_passwords = NULL;
+static GHashTable *intkeyring_ciphertexts = NULL;
+
+static gboolean intkeyring_opened = FALSE;
+static gboolean intkeyring_unlocked = FALSE;
+
+static GList *intkeyring_pending_requests = NULL;
+static void *intkeyring_masterpw_uirequest = NULL;
+
static PurpleKeyring *keyring_handler = NULL;
+static void
+intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+ gpointer data);
+static void
+intkeyring_save(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data);
+static void
+intkeyring_reencrypt_passwords(void);
+static void
+intkeyring_unlock(const gchar *message);
+
+static void
+intkeyring_request_free(intkeyring_request *req)
+{
+ g_return_if_fail(req != NULL);
+
+ purple_str_wipe(req->password);
+ g_free(req);
+}
+
static intkeyring_buff_t *
intkeyring_buff_new(guchar *data, size_t len)
{
@@ -71,12 +118,30 @@ intkeyring_buff_new(guchar *data, size_t len)
static void
intkeyring_buff_free(intkeyring_buff_t *buff)
{
+ if (buff == NULL)
+ return;
+
memset(buff->data, 0, buff->len);
g_free(buff->data);
g_free(buff);
}
static intkeyring_buff_t *
+intkeyring_buff_from_base64(const gchar *base64)
+{
+ guchar *data;
+ gsize len;
+
+ data = purple_base64_decode(base64, &len);
+
+ return intkeyring_buff_new(data, len);
+}
+
+/************************************************************************/
+/* Generic encryption stuff */
+/************************************************************************/
+
+static intkeyring_buff_t *
intkeyring_derive_key(const gchar *passphrase, intkeyring_buff_t *salt)
{
PurpleCipherContext *context;
@@ -137,7 +202,27 @@ intkeyring_gen_salt(size_t len)
return ret;
}
-/* TODO: describe encrypted contents structure */
+/**
+ * Encrypts a plaintext using the specified key.
+ *
+ * Random IV will be generated and stored with ciphertext.
+ *
+ * Encryption scheme:
+ * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++
+ * [ control string ] ++ [ pkcs7 padding ] )
+ * where:
+ * IV: Random, 128bit IV.
+ * plaintext: The plaintext.
+ * min length padding: The padding used to hide the rough length of short
+ * plaintexts, may have a length of 0.
+ * control string: Constant string, verifies corectness of decryption.
+ * pkcs7 padding: The padding used to determine total length of encrypted
+ * content (also provides some verification).
+ *
+ * @param key The AES key.
+ * @param str The NUL-terminated plaintext.
+ * @return The ciphertext with IV, encoded as base64. Must be g_free'd.
+ */
static gchar *
intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
{
@@ -153,14 +238,20 @@ intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
text_len = strlen(str);
verify_len = strlen(INTKEYRING_VERIFY_STR);
- g_return_val_if_fail(text_len + verify_len <= sizeof(plaintext), NULL);
+ plaintext_len = INTKEYRING_ENCRYPTED_MIN_LEN;
+ if (plaintext_len < text_len)
+ plaintext_len = text_len;
+
+ g_return_val_if_fail(plaintext_len + verify_len <= sizeof(plaintext),
+ NULL);
context = purple_cipher_context_new_by_name("aes", NULL);
g_return_val_if_fail(context != NULL, NULL);
+ memset(plaintext, 0, plaintext_len);
memcpy(plaintext, str, text_len);
- memcpy(plaintext + text_len, INTKEYRING_VERIFY_STR, verify_len);
- plaintext_len = text_len + verify_len;
+ memcpy(plaintext + plaintext_len, INTKEYRING_VERIFY_STR, verify_len);
+ plaintext_len += verify_len;
iv = intkeyring_gen_salt(purple_cipher_context_get_block_size(context));
purple_cipher_context_set_iv(context, iv->data, iv->len);
@@ -243,27 +334,267 @@ intkeyring_decrypt(intkeyring_buff_t *key, const gchar *str)
return ret;
}
-static void
-intkeyring_change_master_password(const gchar *new_password)
+/************************************************************************/
+/* Password encryption */
+/************************************************************************/
+
+static gboolean
+intkeyring_change_masterpw(const gchar *new_password)
{
intkeyring_buff_t *salt, *key;
- gchar *verifier, *test;
+ gchar *verifier = NULL, *salt_b64 = NULL;
+ int old_iter;
+ gboolean succ = TRUE;;
+
+ g_return_val_if_fail(intkeyring_unlocked, FALSE);
+
+ old_iter = purple_prefs_get_int(INTKEYRING_PREFS "pbkdf2_iterations");
+ purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations",
+ purple_prefs_get_int(INTKEYRING_PREFS
+ "pbkdf2_desired_iterations"));
salt = intkeyring_gen_salt(32);
key = intkeyring_derive_key(new_password, salt);
- /* In fact, verify str will be concatenated twice before encryption
- * (it's used as a suffix in encryption routine), but it's not
- * a problem.
- */
- verifier = intkeyring_encrypt(key, INTKEYRING_VERIFY_STR);
- purple_debug_info("test-tmp", "verifier=[%s]\n", verifier);
+ if (salt && key && key->len == INTKEYRING_KEY_LEN) {
+ /* In fact, verify str will be concatenated twice before
+ * encryption (it's used as a suffix in encryption routine),
+ * but it's not a problem.
+ */
+ verifier = intkeyring_encrypt(key, INTKEYRING_VERIFY_STR);
+ salt_b64 = purple_base64_encode(salt->data, salt->len);
+ }
+
+ if (!verifier || !salt_b64) {
+ purple_debug_error("keyring-internal", "Failed to change "
+ "master password\n");
+ succ = FALSE;
+ purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations",
+ old_iter);
+ } else {
+ purple_prefs_set_string(INTKEYRING_PREFS "pbkdf2_salt",
+ salt_b64);
+ purple_prefs_set_string(INTKEYRING_PREFS "key_verifier",
+ verifier);
+
+ intkeyring_buff_free(intkeyring_key);
+ intkeyring_key = key;
+ key = NULL;
+
+ intkeyring_reencrypt_passwords();
+
+ purple_signal_emit(purple_keyring_get_handle(),
+ "password-migration", NULL);
+ }
+
+ g_free(salt_b64);
+ g_free(verifier);
+ intkeyring_buff_free(salt);
+ intkeyring_buff_free(key);
+
+ return succ;
+}
+
+static void
+intkeyring_process_queue(void)
+{
+ GList *requests, *it;
+ gboolean open = intkeyring_unlocked;
+
+ requests = g_list_first(intkeyring_pending_requests);
+ intkeyring_pending_requests = NULL;
+
+ for (it = requests; it != NULL; it = g_list_next(it))
+ {
+ intkeyring_request *req = it->data;
+
+ if (open && req->type == INTKEYRING_REQUEST_READ) {
+ intkeyring_read(req->account, req->cb.read,
+ req->cb_data);
+ } else if (open && req->type == INTKEYRING_REQUEST_SAVE) {
+ intkeyring_save(req->account, req->password,
+ req->cb.save, req->cb_data);
+ } else if (open)
+ g_assert_not_reached();
+ else if (req->cb.read != NULL /* || req->cb.write != NULL */ ) {
+ GError *error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_CANCELLED,
+ "Operation cancelled.");
+ if (req->type == INTKEYRING_REQUEST_READ) {
+ req->cb.read(req->account, NULL, error,
+ req->cb_data);
+ } else if (req->type == INTKEYRING_REQUEST_SAVE)
+ req->cb.save(req->account, error, req->cb_data);
+ else
+ g_assert_not_reached();
+ g_error_free(error);
+ }
+
+ intkeyring_request_free(req);
+ }
+ g_list_free(requests);
+}
+
+static void
+intkeyring_decrypt_password(PurpleAccount *account, const gchar *ciphertext)
+{
+ gchar *plaintext;
+
+ plaintext = intkeyring_decrypt(intkeyring_key, ciphertext);
+ if (plaintext == NULL) {
+ purple_debug_warning("keyring-internal",
+ "Failed to decrypt a password\n");
+ return;
+ }
+
+ g_hash_table_replace(intkeyring_passwords, account, plaintext);
+}
+
+static void
+intkeyring_encrypt_password_if_needed(PurpleAccount *account)
+{
+ const gchar *plaintext;
+ gchar *ciphertext;
+
+ ciphertext = g_hash_table_lookup(intkeyring_ciphertexts, account);
+ if (ciphertext != NULL)
+ return;
+
+ plaintext = g_hash_table_lookup(intkeyring_passwords, account);
+ if (plaintext == NULL)
+ return;
+
+ ciphertext = intkeyring_encrypt(intkeyring_key, plaintext);
+ g_return_if_fail(ciphertext != NULL);
+
+ g_hash_table_replace(intkeyring_ciphertexts, account, ciphertext);
+}
+
+static void
+intkeyring_encrypt_passwords_if_needed_it(gpointer account, gpointer plaintext,
+ gpointer _unused)
+{
+ intkeyring_encrypt_password_if_needed(account);
+}
+
+static void
+intkeyring_reencrypt_passwords(void)
+{
+ g_hash_table_remove_all(intkeyring_ciphertexts);
+ g_hash_table_foreach(intkeyring_passwords,
+ intkeyring_encrypt_passwords_if_needed_it, NULL);
+}
+
+static void
+intkeyring_unlock_decrypt(gpointer account, gpointer ciphertext,
+ gpointer _unused)
+{
+ intkeyring_decrypt_password(account, ciphertext);
+}
+
+/************************************************************************/
+/* Opening and unlocking keyring */
+/************************************************************************/
+
+static void
+intkeyring_unlock_ok(gpointer _unused,
+ PurpleRequestFields *fields)
+{
+ const gchar *masterpw;
+ gchar *verifier;
+ intkeyring_buff_t *salt, *key;
+
+ intkeyring_masterpw_uirequest = NULL;
+
+ if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS
+ "encryption_method"), INTKEYRING_ENCRYPTION_METHOD) != 0) {
+ purple_notify_error(NULL,
+ _("Unlocking internal keyring"),
+ _("Selected encryption method is not supported."),
+ _("Most probably, your passwords were encrypted with "
+ "newer Pidgin/libpurple version, please update."));
+ return;
+ }
+
+ masterpw = purple_request_fields_get_string(fields, "password");
+
+ if (masterpw == NULL || masterpw[0] == '\0') {
+ intkeyring_unlock(_("No password entered."));
+ return;
+ }
+
+ salt = intkeyring_buff_from_base64(purple_prefs_get_string(
+ INTKEYRING_PREFS "pbkdf2_salt"));
+ key = intkeyring_derive_key(masterpw, salt);
+ intkeyring_buff_free(salt);
- test = intkeyring_decrypt(key, verifier);
- purple_debug_info("test-tmp", "test=[%s]\n", test);
+ verifier = intkeyring_decrypt(key, purple_prefs_get_string(
+ INTKEYRING_PREFS "key_verifier"));
+
+ if (g_strcmp0(verifier, INTKEYRING_VERIFY_STR) != 0) {
+ g_free(verifier);
+ intkeyring_buff_free(key);
+ intkeyring_unlock(_("Invalid master password entered, "
+ "try again."));
+ return;
+ }
- g_free(test);
g_free(verifier);
+ intkeyring_key = key;
+ intkeyring_unlocked = TRUE;
+
+ g_hash_table_foreach(intkeyring_ciphertexts,
+ intkeyring_unlock_decrypt, NULL);
+
+ intkeyring_process_queue();
+}
+
+static void
+intkeyring_unlock_cancel(gpointer _unused,
+ PurpleRequestFields *fields)
+{
+ intkeyring_masterpw_uirequest = NULL;
+ intkeyring_process_queue();
+}
+
+static void
+intkeyring_unlock(const gchar *message)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+ const gchar *primary_msg, *secondary_msg = NULL;
+
+ if (intkeyring_unlocked || intkeyring_masterpw_uirequest != NULL)
+ return;
+
+ if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) {
+ intkeyring_unlocked = TRUE;
+ intkeyring_process_queue();
+ return;
+ }
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_string_new("password",
+ _("Master password"), "", FALSE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+
+ primary_msg = _("Please, enter master password");
+ if (message) {
+ secondary_msg = primary_msg;
+ primary_msg = message;
+ }
+
+ intkeyring_masterpw_uirequest = purple_request_fields(NULL,
+ _("Unlocking internal keyring"),
+ primary_msg, secondary_msg, fields,
+ _("OK"), G_CALLBACK(intkeyring_unlock_ok),
+ _("Cancel"), G_CALLBACK(intkeyring_unlock_cancel),
+ NULL, NULL, NULL, NULL);
}
static void
@@ -275,8 +606,14 @@ intkeyring_open(void)
intkeyring_passwords = g_hash_table_new_full(g_direct_hash,
g_direct_equal, NULL, (GDestroyNotify)purple_str_wipe);
+ intkeyring_ciphertexts = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, g_free);
}
+/************************************************************************/
+/* Keyring interface implementation */
+/************************************************************************/
+
static void
intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
gpointer data)
@@ -286,6 +623,22 @@ intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
intkeyring_open();
+ if (!intkeyring_unlocked && g_hash_table_lookup(intkeyring_ciphertexts,
+ account) != NULL)
+ {
+ intkeyring_request *req = g_new0(intkeyring_request, 1);
+
+ req->type = INTKEYRING_REQUEST_READ;
+ req->account = account;
+ req->cb.read = cb;
+ req->cb_data = data;
+ intkeyring_pending_requests =
+ g_list_append(intkeyring_pending_requests, req);
+
+ intkeyring_unlock(NULL);
+ return;
+ }
+
password = g_hash_table_lookup(intkeyring_passwords, account);
if (password != NULL) {
@@ -315,8 +668,34 @@ intkeyring_save(PurpleAccount *account, const gchar *password,
PurpleKeyringSaveCallback cb, gpointer data)
{
void *old_password;
+
intkeyring_open();
+ if (!intkeyring_unlocked) {
+ if (password == NULL) {
+ g_hash_table_remove(intkeyring_ciphertexts, account);
+ g_hash_table_remove(intkeyring_passwords, account);
+ if (cb)
+ cb(account, NULL, data);
+ return;
+ }
+
+ intkeyring_request *req = g_new0(intkeyring_request, 1);
+
+ req->type = INTKEYRING_REQUEST_SAVE;
+ req->account = account;
+ req->password = g_strdup(password);
+ req->cb.save = cb;
+ req->cb_data = data;
+ intkeyring_pending_requests =
+ g_list_append(intkeyring_pending_requests, req);
+
+ intkeyring_unlock(NULL);
+ return;
+ }
+
+ g_hash_table_remove(intkeyring_ciphertexts, account);
+
old_password = g_hash_table_lookup(intkeyring_passwords, account);
if (password == NULL)
@@ -326,6 +705,8 @@ intkeyring_save(PurpleAccount *account, const gchar *password,
g_strdup(password));
}
+ intkeyring_encrypt_password_if_needed(account);
+
if (!(password == NULL && old_password == NULL)) {
purple_debug_misc("keyring-internal",
"Password %s for account %s (%s).\n",
@@ -350,9 +731,21 @@ intkeyring_close(void)
if (!intkeyring_opened)
return;
intkeyring_opened = FALSE;
+ intkeyring_unlocked = FALSE;
+ if (intkeyring_masterpw_uirequest) {
+ purple_request_close(PURPLE_REQUEST_FIELDS,
+ intkeyring_masterpw_uirequest);
+ }
+ g_warn_if_fail(intkeyring_masterpw_uirequest == NULL);
+ g_warn_if_fail(intkeyring_pending_requests == NULL);
+
+ intkeyring_buff_free(intkeyring_key);
+ intkeyring_key = NULL;
g_hash_table_destroy(intkeyring_passwords);
intkeyring_passwords = NULL;
+ g_hash_table_destroy(intkeyring_ciphertexts);
+ intkeyring_ciphertexts = NULL;
}
static gboolean
@@ -371,6 +764,14 @@ intkeyring_import_password(PurpleAccount *account, const char *mode,
g_hash_table_replace(intkeyring_passwords, account,
g_strdup(data));
return TRUE;
+ } else if (g_strcmp0(mode, "ciphertext") == 0) {
+ if (intkeyring_unlocked)
+ intkeyring_decrypt_password(account, data);
+ else {
+ g_hash_table_replace(intkeyring_ciphertexts, account,
+ g_strdup(data));
+ }
+ return TRUE;
} else {
if (error != NULL) {
*error = g_error_new(PURPLE_KEYRING_ERROR,
@@ -385,20 +786,44 @@ static gboolean
intkeyring_export_password(PurpleAccount *account, const char **mode,
char **data, GError **error, GDestroyNotify *destroy)
{
- gchar *password;
-
+ gchar *ciphertext = NULL;
intkeyring_open();
- password = g_hash_table_lookup(intkeyring_passwords, account);
+ if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) {
+ gchar *cleartext = g_hash_table_lookup(intkeyring_passwords,
+ account);
+
+ if (cleartext == NULL)
+ return FALSE;
- if (password == NULL) {
- return FALSE;
- } else {
*mode = "cleartext";
- *data = g_strdup(password);
+ *data = g_strdup(cleartext);
*destroy = (GDestroyNotify)purple_str_wipe;
return TRUE;
}
+
+ ciphertext = g_strdup(g_hash_table_lookup(intkeyring_ciphertexts,
+ account));
+
+ if (ciphertext == NULL && intkeyring_unlocked) {
+ gchar *plaintext = g_hash_table_lookup(intkeyring_passwords,
+ account);
+
+ if (plaintext == NULL)
+ return FALSE;
+
+ purple_debug_warning("keyring-internal", "Encrypted password "
+ "is missing at export (it shouldn't happen)\n");
+ ciphertext = intkeyring_encrypt(intkeyring_key, plaintext);
+ }
+
+ if (ciphertext == NULL)
+ return FALSE;
+
+ *mode = "ciphertext";
+ *data = ciphertext;
+ *destroy = (GDestroyNotify)g_free;
+ return TRUE;
}
static PurpleRequestFields *
@@ -449,6 +874,10 @@ intkeyring_apply_settings(void *notify_handle,
{
const gchar *passphrase, *passphrase2;
+ intkeyring_unlock(_("You have to unlock the keyring first."));
+ if (!intkeyring_unlocked)
+ return FALSE;
+
passphrase = purple_request_fields_get_string(fields, "passphrase1");
if (g_strcmp0(passphrase, "") == 0)
passphrase = NULL;
@@ -463,7 +892,9 @@ intkeyring_apply_settings(void *notify_handle,
return FALSE;
}
- if (purple_request_fields_get_bool(fields, "encrypt_passwords") && !passphrase) {
+ if (purple_request_fields_get_bool(fields, "encrypt_passwords") &&
+ !passphrase && !intkeyring_key)
+ {
purple_notify_error(notify_handle,
_("Internal keyring settings"),
_("You have to set up a Master password, if you want "
@@ -471,15 +902,34 @@ intkeyring_apply_settings(void *notify_handle,
return FALSE;
}
- purple_prefs_set_bool(INTKEYRING_PREFS "encrypt_passwords",
- purple_request_fields_get_bool(fields, "encrypt_passwords"));
+ if (!purple_request_fields_get_bool(fields, "encrypt_passwords") &&
+ passphrase)
+ {
+ purple_notify_error(notify_handle,
+ _("Internal keyring settings"),
+ _("You don't need any master password, if you won't "
+ "enable passwords encryption"), NULL);
+ return FALSE;
+ }
+
+ purple_prefs_set_string(INTKEYRING_PREFS "encryption_method",
+ INTKEYRING_ENCRYPTION_METHOD);
purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_desired_iterations",
purple_request_fields_get_integer(fields,
"pbkdf2_desired_iterations"));
- if (passphrase)
- intkeyring_change_master_password(passphrase);
+ if (passphrase != NULL) {
+ if (!intkeyring_change_masterpw(passphrase))
+ return FALSE;
+ }
+
+ purple_prefs_set_bool(INTKEYRING_PREFS "encrypt_passwords",
+ purple_request_fields_get_bool(fields, "encrypt_passwords"));
+
+ purple_signal_emit(purple_keyring_get_handle(), "password-migration",
+ NULL);
+
return TRUE;
}
@@ -526,6 +976,13 @@ intkeyring_unload(PurplePlugin *plugin)
purple_keyring_free(keyring_handler);
keyring_handler = NULL;
+ if (intkeyring_key != NULL) {
+ purple_debug_warning("keyring-internal", "Master key should be "
+ "cleaned up at this point\n");
+ intkeyring_buff_free(intkeyring_key);
+ intkeyring_key = NULL;
+ }
+
return TRUE;
}
@@ -560,10 +1017,10 @@ static void
init_plugin(PurplePlugin *plugin)
{
purple_prefs_add_none("/plugins/keyrings");
- purple_prefs_add_none(INTKEYRING_PREFS);
+ purple_prefs_add_none("/plugins/keyrings/internal");
purple_prefs_add_bool(INTKEYRING_PREFS "encrypt_passwords", FALSE);
purple_prefs_add_string(INTKEYRING_PREFS "encryption_method",
- "pbkdf2-sha256-aes256");
+ INTKEYRING_ENCRYPTION_METHOD);
purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_desired_iterations",
INTKEYRING_PBKDF2_ITERATIONS);
purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_iterations",