2 * @file internalkeyring.c internal keyring
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program ; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
34 #include "ciphers/aescipher.h"
35 #include "ciphers/pbkdf2cipher.h"
36 #include "ciphers/sha256hash.h"
38 #define INTKEYRING_NAME N_("Internal keyring")
39 #define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \
40 "storage behaviour for libpurple.")
41 #define INTKEYRING_AUTHORS { "Tomek Wasilczyk <twasilczyk@pidgin.im>",NULL }
42 #define INTKEYRING_ID PURPLE_DEFAULT_KEYRING
43 #define INTKEYRING_DOMAIN (g_quark_from_static_string(INTKEYRING_ID))
45 #define INTKEYRING_VERIFY_STR "[verification-string]"
46 #define INTKEYRING_PBKDF2_ITERATIONS 10000
47 #define INTKEYRING_PBKDF2_ITERATIONS_MIN 1000
48 #define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000
49 #define INTKEYRING_KEY_LEN (256/8)
50 #define INTKEYRING_ENCRYPT_BUFF_LEN 1000
51 #define INTKEYRING_ENCRYPTED_MIN_LEN 50
52 #define INTKEYRING_ENCRYPTION_METHOD "pbkdf2-sha256-aes256"
54 #define INTKEYRING_PREFS "/plugins/keyrings/internal/"
56 /* win32 build defines such macro to override read() routine */
63 INTKEYRING_REQUEST_READ
,
64 INTKEYRING_REQUEST_SAVE
66 PurpleAccount
*account
;
70 PurpleKeyringReadCallback read
;
71 PurpleKeyringSaveCallback save
;
82 static intkeyring_buff_t
*intkeyring_key
;
83 static GHashTable
*intkeyring_passwords
= NULL
;
84 static GHashTable
*intkeyring_ciphertexts
= NULL
;
86 static gboolean intkeyring_opened
= FALSE
;
87 static gboolean intkeyring_unlocked
= FALSE
;
89 static GList
*intkeyring_pending_requests
= NULL
;
90 static void *intkeyring_masterpw_uirequest
= NULL
;
92 static PurpleKeyring
*keyring_handler
= NULL
;
95 intkeyring_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
98 intkeyring_save(PurpleAccount
*account
, const gchar
*password
,
99 PurpleKeyringSaveCallback cb
, gpointer data
);
101 intkeyring_reencrypt_passwords(void);
103 intkeyring_unlock(const gchar
*message
);
106 intkeyring_request_free(intkeyring_request
*req
)
108 g_return_if_fail(req
!= NULL
);
110 purple_str_wipe(req
->password
);
114 static intkeyring_buff_t
*
115 intkeyring_buff_new(guchar
*data
, size_t len
)
117 intkeyring_buff_t
*ret
= g_new(intkeyring_buff_t
, 1);
126 intkeyring_buff_free(intkeyring_buff_t
*buff
)
131 memset(buff
->data
, 0, buff
->len
);
136 static intkeyring_buff_t
*
137 intkeyring_buff_from_base64(const gchar
*base64
)
142 data
= purple_base64_decode(base64
, &len
);
144 return intkeyring_buff_new(data
, len
);
147 /************************************************************************/
148 /* Generic encryption stuff */
149 /************************************************************************/
151 static intkeyring_buff_t
*
152 intkeyring_derive_key(const gchar
*passphrase
, intkeyring_buff_t
*salt
)
154 PurpleCipher
*cipher
;
157 intkeyring_buff_t
*ret
;
159 g_return_val_if_fail(passphrase
!= NULL
, NULL
);
161 hash
= purple_sha256_hash_new();
162 cipher
= purple_pbkdf2_cipher_new(hash
);
164 g_object_set(G_OBJECT(cipher
), "iter_count",
165 GUINT_TO_POINTER(purple_prefs_get_int(INTKEYRING_PREFS
166 "pbkdf2_iterations")), NULL
);
167 g_object_set(G_OBJECT(cipher
), "out_len", GUINT_TO_POINTER(
168 INTKEYRING_KEY_LEN
), NULL
);
169 purple_cipher_set_salt(cipher
, salt
->data
, salt
->len
);
170 purple_cipher_set_key(cipher
, (const guchar
*)passphrase
,
173 ret
= intkeyring_buff_new(g_new(guchar
, INTKEYRING_KEY_LEN
),
175 succ
= purple_cipher_digest(cipher
, ret
->data
, ret
->len
);
177 g_object_unref(cipher
);
178 g_object_unref(hash
);
181 intkeyring_buff_free(ret
);
188 static intkeyring_buff_t
*
189 intkeyring_gen_salt(size_t len
)
191 intkeyring_buff_t
*ret
;
194 g_return_val_if_fail(len
> 0, NULL
);
196 ret
= intkeyring_buff_new(g_new(guchar
, len
), len
);
198 while (filled
< len
) {
199 guint32 r
= g_random_int();
202 for (i
= 0; i
< 4; i
++) {
203 ret
->data
[filled
++] = r
& 0xFF;
214 * Encrypts a plaintext using the specified key.
216 * Random IV will be generated and stored with ciphertext.
219 * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++
220 * [ control string ] ++ [ pkcs7 padding ] )
222 * IV: Random, 128bit IV.
223 * plaintext: The plaintext.
224 * min length padding: The padding used to hide the rough length of short
225 * plaintexts, may have a length of 0.
226 * control string: Constant string, verifies corectness of decryption.
227 * pkcs7 padding: The padding used to determine total length of encrypted
228 * content (also provides some verification).
230 * @param key The AES key.
231 * @param str The NUL-terminated plaintext.
232 * @return The ciphertext with IV, encoded as base64. Must be g_free'd.
235 intkeyring_encrypt(intkeyring_buff_t
*key
, const gchar
*str
)
237 PurpleCipher
*cipher
;
238 intkeyring_buff_t
*iv
;
239 guchar plaintext
[INTKEYRING_ENCRYPT_BUFF_LEN
];
240 size_t plaintext_len
, text_len
, verify_len
;
241 guchar encrypted_raw
[INTKEYRING_ENCRYPT_BUFF_LEN
];
242 ssize_t encrypted_size
;
244 g_return_val_if_fail(key
!= NULL
, NULL
);
245 g_return_val_if_fail(str
!= NULL
, NULL
);
247 text_len
= strlen(str
);
248 verify_len
= strlen(INTKEYRING_VERIFY_STR
);
249 plaintext_len
= INTKEYRING_ENCRYPTED_MIN_LEN
;
250 if (plaintext_len
< text_len
)
251 plaintext_len
= text_len
;
253 g_return_val_if_fail(plaintext_len
+ verify_len
<= sizeof(plaintext
),
256 cipher
= purple_aes_cipher_new();
257 g_return_val_if_fail(cipher
!= NULL
, NULL
);
259 memset(plaintext
, 0, plaintext_len
);
260 memcpy(plaintext
, str
, text_len
);
261 memcpy(plaintext
+ plaintext_len
, INTKEYRING_VERIFY_STR
, verify_len
);
262 plaintext_len
+= verify_len
;
264 iv
= intkeyring_gen_salt(purple_cipher_get_block_size(cipher
));
265 g_return_val_if_fail(iv
!= NULL
, NULL
);
266 purple_cipher_set_iv(cipher
, iv
->data
, iv
->len
);
267 purple_cipher_set_key(cipher
, key
->data
, key
->len
);
268 purple_cipher_set_batch_mode(cipher
,
269 PURPLE_CIPHER_BATCH_MODE_CBC
);
271 memcpy(encrypted_raw
, iv
->data
, iv
->len
);
273 encrypted_size
= purple_cipher_encrypt(cipher
,
274 plaintext
, plaintext_len
, encrypted_raw
+ iv
->len
,
275 sizeof(encrypted_raw
) - iv
->len
);
276 encrypted_size
+= iv
->len
;
278 memset(plaintext
, 0, plaintext_len
);
279 intkeyring_buff_free(iv
);
280 g_object_unref(cipher
);
282 if (encrypted_size
< 0)
285 return purple_base64_encode(encrypted_raw
, encrypted_size
);
290 intkeyring_decrypt(intkeyring_buff_t
*key
, const gchar
*str
)
292 PurpleCipher
*cipher
;
293 guchar
*encrypted_raw
;
294 gsize encrypted_size
;
295 size_t iv_len
, verify_len
, text_len
;
296 guchar plaintext
[INTKEYRING_ENCRYPT_BUFF_LEN
];
297 const gchar
*verify_str
= NULL
;
298 ssize_t plaintext_len
;
301 g_return_val_if_fail(key
!= NULL
, NULL
);
302 g_return_val_if_fail(str
!= NULL
, NULL
);
304 cipher
= purple_aes_cipher_new();
305 g_return_val_if_fail(cipher
!= NULL
, NULL
);
307 encrypted_raw
= purple_base64_decode(str
, &encrypted_size
);
308 g_return_val_if_fail(encrypted_raw
!= NULL
, NULL
);
310 iv_len
= purple_cipher_get_block_size(cipher
);
311 if (encrypted_size
< iv_len
) {
312 g_free(encrypted_raw
);
316 purple_cipher_set_iv(cipher
, encrypted_raw
, iv_len
);
317 purple_cipher_set_key(cipher
, key
->data
, key
->len
);
318 purple_cipher_set_batch_mode(cipher
,
319 PURPLE_CIPHER_BATCH_MODE_CBC
);
321 plaintext_len
= purple_cipher_decrypt(cipher
,
322 encrypted_raw
+ iv_len
, encrypted_size
- iv_len
,
323 plaintext
, sizeof(plaintext
));
325 g_free(encrypted_raw
);
326 g_object_unref(cipher
);
328 verify_len
= strlen(INTKEYRING_VERIFY_STR
);
329 /* Don't remove the len > 0 check! */
330 if (plaintext_len
> 0 && (gsize
)plaintext_len
> verify_len
&&
331 plaintext
[plaintext_len
] == '\0')
333 verify_str
= (gchar
*)plaintext
+ plaintext_len
- verify_len
;
336 if (g_strcmp0(verify_str
, INTKEYRING_VERIFY_STR
) != 0) {
337 purple_debug_warning("keyring-internal",
338 "Verification failed on decryption\n");
339 memset(plaintext
, 0, sizeof(plaintext
));
342 g_assert(plaintext_len
> 0);
344 text_len
= plaintext_len
- verify_len
;
345 ret
= g_new(gchar
, text_len
+ 1);
346 memcpy(ret
, plaintext
, text_len
);
347 memset(plaintext
, 0, plaintext_len
);
348 ret
[text_len
] = '\0';
353 /************************************************************************/
354 /* Password encryption */
355 /************************************************************************/
358 intkeyring_change_masterpw(const gchar
*new_password
)
360 intkeyring_buff_t
*salt
, *key
;
361 gchar
*verifier
= NULL
, *salt_b64
= NULL
;
363 gboolean succ
= TRUE
;;
365 g_return_val_if_fail(intkeyring_unlocked
, FALSE
);
367 old_iter
= purple_prefs_get_int(INTKEYRING_PREFS
"pbkdf2_iterations");
368 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_iterations",
369 purple_prefs_get_int(INTKEYRING_PREFS
370 "pbkdf2_desired_iterations"));
372 salt
= intkeyring_gen_salt(32);
373 key
= intkeyring_derive_key(new_password
, salt
);
375 if (salt
&& key
&& key
->len
== INTKEYRING_KEY_LEN
) {
376 /* In fact, verify str will be concatenated twice before
377 * encryption (it's used as a suffix in encryption routine),
378 * but it's not a problem.
380 verifier
= intkeyring_encrypt(key
, INTKEYRING_VERIFY_STR
);
381 salt_b64
= purple_base64_encode(salt
->data
, salt
->len
);
384 if (!verifier
|| !salt_b64
) {
385 purple_debug_error("keyring-internal", "Failed to change "
386 "master password\n");
388 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_iterations",
391 purple_prefs_set_string(INTKEYRING_PREFS
"pbkdf2_salt",
393 purple_prefs_set_string(INTKEYRING_PREFS
"key_verifier",
396 intkeyring_buff_free(intkeyring_key
);
397 intkeyring_key
= key
;
400 intkeyring_reencrypt_passwords();
402 purple_signal_emit(purple_keyring_get_handle(),
403 "password-migration", NULL
);
408 intkeyring_buff_free(salt
);
409 intkeyring_buff_free(key
);
415 intkeyring_process_queue(void)
417 GList
*requests
, *it
;
418 gboolean open
= intkeyring_unlocked
;
420 requests
= g_list_first(intkeyring_pending_requests
);
421 intkeyring_pending_requests
= NULL
;
423 for (it
= requests
; it
!= NULL
; it
= g_list_next(it
)) {
424 intkeyring_request
*req
= it
->data
;
426 if (open
&& req
->type
== INTKEYRING_REQUEST_READ
) {
427 intkeyring_read(req
->account
, req
->cb
.read
,
429 } else if (open
&& req
->type
== INTKEYRING_REQUEST_SAVE
) {
430 intkeyring_save(req
->account
, req
->password
,
431 req
->cb
.save
, req
->cb_data
);
433 g_assert_not_reached();
434 else if (req
->cb
.read
!= NULL
/* || req->cb.write != NULL */ ) {
435 GError
*error
= g_error_new(PURPLE_KEYRING_ERROR
,
436 PURPLE_KEYRING_ERROR_CANCELLED
,
437 _("Operation cancelled."));
438 if (req
->type
== INTKEYRING_REQUEST_READ
) {
439 req
->cb
.read(req
->account
, NULL
, error
,
441 } else if (req
->type
== INTKEYRING_REQUEST_SAVE
)
442 req
->cb
.save(req
->account
, error
, req
->cb_data
);
444 g_assert_not_reached();
448 intkeyring_request_free(req
);
450 g_list_free(requests
);
454 intkeyring_decrypt_password(PurpleAccount
*account
, const gchar
*ciphertext
)
458 plaintext
= intkeyring_decrypt(intkeyring_key
, ciphertext
);
459 if (plaintext
== NULL
) {
460 purple_debug_warning("keyring-internal",
461 "Failed to decrypt a password\n");
465 g_hash_table_replace(intkeyring_passwords
, account
, plaintext
);
469 intkeyring_encrypt_password_if_needed(PurpleAccount
*account
)
471 const gchar
*plaintext
;
474 if (intkeyring_key
== NULL
) {
475 g_hash_table_remove(intkeyring_ciphertexts
, account
);
479 ciphertext
= g_hash_table_lookup(intkeyring_ciphertexts
, account
);
480 if (ciphertext
!= NULL
)
483 plaintext
= g_hash_table_lookup(intkeyring_passwords
, account
);
484 if (plaintext
== NULL
)
487 ciphertext
= intkeyring_encrypt(intkeyring_key
, plaintext
);
488 g_return_if_fail(ciphertext
!= NULL
);
490 g_hash_table_replace(intkeyring_ciphertexts
, account
, ciphertext
);
494 intkeyring_encrypt_passwords_if_needed_it(gpointer account
, gpointer plaintext
,
497 intkeyring_encrypt_password_if_needed(account
);
501 intkeyring_reencrypt_passwords(void)
503 g_hash_table_remove_all(intkeyring_ciphertexts
);
504 g_hash_table_foreach(intkeyring_passwords
,
505 intkeyring_encrypt_passwords_if_needed_it
, NULL
);
509 intkeyring_unlock_decrypt(gpointer account
, gpointer ciphertext
,
512 intkeyring_decrypt_password(account
, ciphertext
);
515 /************************************************************************/
516 /* Opening and unlocking keyring */
517 /************************************************************************/
520 intkeyring_unlock_ok(gpointer _unused
,
521 PurpleRequestFields
*fields
)
523 const gchar
*masterpw
;
525 intkeyring_buff_t
*salt
, *key
;
527 intkeyring_masterpw_uirequest
= NULL
;
529 if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS
530 "encryption_method"), INTKEYRING_ENCRYPTION_METHOD
) != 0)
532 purple_notify_error(NULL
,
533 _("Unlocking internal keyring"),
534 _("Selected encryption method is not supported."),
535 _("Most probably, your passwords were encrypted with "
536 "newer Pidgin/libpurple version, please update."),
541 masterpw
= purple_request_fields_get_string(fields
, "password");
543 if (masterpw
== NULL
|| masterpw
[0] == '\0') {
544 intkeyring_unlock(_("No password entered."));
548 salt
= intkeyring_buff_from_base64(purple_prefs_get_string(
549 INTKEYRING_PREFS
"pbkdf2_salt"));
550 key
= intkeyring_derive_key(masterpw
, salt
);
551 intkeyring_buff_free(salt
);
553 verifier
= intkeyring_decrypt(key
, purple_prefs_get_string(
554 INTKEYRING_PREFS
"key_verifier"));
556 if (g_strcmp0(verifier
, INTKEYRING_VERIFY_STR
) != 0) {
558 intkeyring_buff_free(key
);
559 intkeyring_unlock(_("Invalid master password entered, "
565 intkeyring_key
= key
;
566 intkeyring_unlocked
= TRUE
;
568 g_hash_table_foreach(intkeyring_ciphertexts
,
569 intkeyring_unlock_decrypt
, NULL
);
571 intkeyring_process_queue();
575 intkeyring_unlock_cancel(gpointer _unused
,
576 PurpleRequestFields
*fields
)
578 intkeyring_masterpw_uirequest
= NULL
;
579 intkeyring_process_queue();
583 intkeyring_unlock(const gchar
*message
)
585 PurpleRequestFields
*fields
;
586 PurpleRequestFieldGroup
*group
;
587 PurpleRequestField
*field
;
588 const gchar
*primary_msg
, *secondary_msg
= NULL
;
590 if (intkeyring_unlocked
|| intkeyring_masterpw_uirequest
!= NULL
)
593 if (!purple_prefs_get_bool(INTKEYRING_PREFS
"encrypt_passwords")) {
594 intkeyring_unlocked
= TRUE
;
595 intkeyring_process_queue();
599 fields
= purple_request_fields_new();
600 group
= purple_request_field_group_new(NULL
);
601 purple_request_fields_add_group(fields
, group
);
603 field
= purple_request_field_string_new("password",
604 _("Master password"), "", FALSE
);
605 purple_request_field_string_set_masked(field
, TRUE
);
606 purple_request_field_group_add_field(group
, field
);
608 primary_msg
= _("Please, enter master password");
610 secondary_msg
= primary_msg
;
611 primary_msg
= message
;
614 intkeyring_masterpw_uirequest
= purple_request_fields(NULL
,
615 _("Unlocking internal keyring"),
616 primary_msg
, secondary_msg
, fields
,
617 _("OK"), G_CALLBACK(intkeyring_unlock_ok
),
618 _("Cancel"), G_CALLBACK(intkeyring_unlock_cancel
),
623 intkeyring_open(void)
625 if (intkeyring_opened
)
627 intkeyring_opened
= TRUE
;
629 intkeyring_passwords
= g_hash_table_new_full(g_direct_hash
,
630 g_direct_equal
, NULL
, (GDestroyNotify
)purple_str_wipe
);
631 intkeyring_ciphertexts
= g_hash_table_new_full(g_direct_hash
,
632 g_direct_equal
, NULL
, g_free
);
635 /************************************************************************/
636 /* Keyring interface implementation */
637 /************************************************************************/
640 intkeyring_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
643 const char *password
;
648 if (!intkeyring_unlocked
&& g_hash_table_lookup(intkeyring_ciphertexts
,
651 intkeyring_request
*req
= g_new0(intkeyring_request
, 1);
653 req
->type
= INTKEYRING_REQUEST_READ
;
654 req
->account
= account
;
657 intkeyring_pending_requests
=
658 g_list_append(intkeyring_pending_requests
, req
);
660 intkeyring_unlock(NULL
);
664 password
= g_hash_table_lookup(intkeyring_passwords
, account
);
666 if (password
!= NULL
) {
667 purple_debug_misc("keyring-internal",
668 "Got password for account %s (%s).\n",
669 purple_account_get_username(account
),
670 purple_account_get_protocol_id(account
));
672 cb(account
, password
, NULL
, data
);
674 if (purple_debug_is_verbose()) {
675 purple_debug_misc("keyring-internal",
676 "No password for account %s (%s).\n",
677 purple_account_get_username(account
),
678 purple_account_get_protocol_id(account
));
680 error
= g_error_new(PURPLE_KEYRING_ERROR
,
681 PURPLE_KEYRING_ERROR_NOPASSWORD
,
682 _("Password not found."));
684 cb(account
, NULL
, error
, data
);
690 intkeyring_save(PurpleAccount
*account
, const gchar
*password
,
691 PurpleKeyringSaveCallback cb
, gpointer data
)
697 if (!intkeyring_unlocked
) {
698 intkeyring_request
*req
;
700 if (password
== NULL
) {
701 g_hash_table_remove(intkeyring_ciphertexts
, account
);
702 g_hash_table_remove(intkeyring_passwords
, account
);
704 cb(account
, NULL
, data
);
708 req
= g_new0(intkeyring_request
, 1);
709 req
->type
= INTKEYRING_REQUEST_SAVE
;
710 req
->account
= account
;
711 req
->password
= g_strdup(password
);
714 intkeyring_pending_requests
=
715 g_list_append(intkeyring_pending_requests
, req
);
717 intkeyring_unlock(NULL
);
721 g_hash_table_remove(intkeyring_ciphertexts
, account
);
723 old_password
= g_hash_table_lookup(intkeyring_passwords
, account
);
725 if (password
== NULL
)
726 g_hash_table_remove(intkeyring_passwords
, account
);
728 g_hash_table_replace(intkeyring_passwords
, account
,
732 intkeyring_encrypt_password_if_needed(account
);
734 if (!(password
== NULL
&& old_password
== NULL
)) {
735 purple_debug_misc("keyring-internal",
736 "Password %s for account %s (%s).\n",
737 (password
== NULL
? "removed" : (old_password
== NULL
?
738 "saved" : "updated")),
739 purple_account_get_username(account
),
740 purple_account_get_protocol_id(account
));
741 } else if (purple_debug_is_verbose()) {
742 purple_debug_misc("keyring-internal",
743 "Password for account %s (%s) was already removed.\n",
744 purple_account_get_username(account
),
745 purple_account_get_protocol_id(account
));
749 cb(account
, NULL
, data
);
753 intkeyring_close(void)
755 if (!intkeyring_opened
)
757 intkeyring_opened
= FALSE
;
758 intkeyring_unlocked
= FALSE
;
760 if (intkeyring_masterpw_uirequest
) {
761 purple_request_close(PURPLE_REQUEST_FIELDS
,
762 intkeyring_masterpw_uirequest
);
764 g_warn_if_fail(intkeyring_masterpw_uirequest
== NULL
);
765 g_warn_if_fail(intkeyring_pending_requests
== NULL
);
767 intkeyring_buff_free(intkeyring_key
);
768 intkeyring_key
= NULL
;
769 g_hash_table_destroy(intkeyring_passwords
);
770 intkeyring_passwords
= NULL
;
771 g_hash_table_destroy(intkeyring_ciphertexts
);
772 intkeyring_ciphertexts
= NULL
;
776 intkeyring_import_password(PurpleAccount
*account
, const char *mode
,
777 const char *data
, GError
**error
)
779 g_return_val_if_fail(account
!= NULL
, FALSE
);
780 g_return_val_if_fail(data
!= NULL
, FALSE
);
787 if (g_strcmp0(mode
, "cleartext") == 0) {
788 g_hash_table_replace(intkeyring_passwords
, account
,
791 } else if (g_strcmp0(mode
, "ciphertext") == 0) {
792 if (intkeyring_unlocked
)
793 intkeyring_decrypt_password(account
, data
);
795 g_hash_table_replace(intkeyring_ciphertexts
, account
,
801 *error
= g_error_new(PURPLE_KEYRING_ERROR
,
802 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
803 _("Invalid password storage mode."));
810 intkeyring_export_password(PurpleAccount
*account
, const char **mode
,
811 char **data
, GError
**error
, GDestroyNotify
*destroy
)
813 gchar
*ciphertext
= NULL
;
816 if (!purple_prefs_get_bool(INTKEYRING_PREFS
"encrypt_passwords")) {
817 gchar
*cleartext
= g_hash_table_lookup(intkeyring_passwords
,
820 if (cleartext
== NULL
)
824 *data
= g_strdup(cleartext
);
825 *destroy
= (GDestroyNotify
)purple_str_wipe
;
829 ciphertext
= g_strdup(g_hash_table_lookup(intkeyring_ciphertexts
,
832 if (ciphertext
== NULL
&& intkeyring_unlocked
) {
833 gchar
*plaintext
= g_hash_table_lookup(intkeyring_passwords
,
836 if (plaintext
== NULL
)
839 purple_debug_warning("keyring-internal", "Encrypted password "
840 "is missing at export (it shouldn't happen)\n");
841 ciphertext
= intkeyring_encrypt(intkeyring_key
, plaintext
);
844 if (ciphertext
== NULL
)
847 *mode
= "ciphertext";
849 *destroy
= (GDestroyNotify
)g_free
;
853 static PurpleRequestFields
*
854 intkeyring_read_settings(void)
856 PurpleRequestFields
*fields
;
857 PurpleRequestFieldGroup
*group
;
858 PurpleRequestField
*field
;
860 fields
= purple_request_fields_new();
861 group
= purple_request_field_group_new(NULL
);
862 purple_request_fields_add_group(fields
, group
);
864 field
= purple_request_field_bool_new("encrypt_passwords",
865 _("Encrypt passwords"), purple_prefs_get_bool(
866 INTKEYRING_PREFS
"encrypt_passwords"));
867 purple_request_field_group_add_field(group
, field
);
869 group
= purple_request_field_group_new(_("Master password"));
870 purple_request_fields_add_group(fields
, group
);
872 field
= purple_request_field_string_new("passphrase1",
873 _("New passphrase:"), "", FALSE
);
874 purple_request_field_string_set_masked(field
, TRUE
);
875 purple_request_field_group_add_field(group
, field
);
877 field
= purple_request_field_string_new("passphrase2",
878 _("New passphrase (again):"), "", FALSE
);
879 purple_request_field_string_set_masked(field
, TRUE
);
880 purple_request_field_group_add_field(group
, field
);
882 group
= purple_request_field_group_new(_("Advanced settings"));
883 purple_request_fields_add_group(fields
, group
);
885 field
= purple_request_field_int_new("pbkdf2_desired_iterations",
886 _("Number of PBKDF2 iterations:"), purple_prefs_get_int(
887 INTKEYRING_PREFS
"pbkdf2_desired_iterations"),
888 INTKEYRING_PBKDF2_ITERATIONS_MIN
,
889 INTKEYRING_PBKDF2_ITERATIONS_MAX
);
890 purple_request_field_group_add_field(group
, field
);
896 intkeyring_apply_settings(void *notify_handle
,
897 PurpleRequestFields
*fields
)
899 const gchar
*passphrase
, *passphrase2
;
901 intkeyring_unlock(_("You have to unlock the keyring first."));
902 if (!intkeyring_unlocked
)
905 passphrase
= purple_request_fields_get_string(fields
, "passphrase1");
906 if (g_strcmp0(passphrase
, "") == 0)
908 passphrase2
= purple_request_fields_get_string(fields
, "passphrase2");
909 if (g_strcmp0(passphrase2
, "") == 0)
912 if (g_strcmp0(passphrase
, passphrase2
) != 0) {
913 purple_notify_error(notify_handle
,
914 _("Internal keyring settings"),
915 _("Passphrases do not match"), NULL
, NULL
);
919 if (purple_request_fields_get_bool(fields
, "encrypt_passwords") &&
920 !passphrase
&& !intkeyring_key
)
922 purple_notify_error(notify_handle
,
923 _("Internal keyring settings"),
924 _("You have to set up a Master password, if you want "
925 "to enable encryption"), NULL
, NULL
);
929 if (!purple_request_fields_get_bool(fields
, "encrypt_passwords") &&
932 purple_notify_error(notify_handle
,
933 _("Internal keyring settings"),
934 _("You don't need any master password, if you won't "
935 "enable passwords encryption"), NULL
, NULL
);
939 purple_prefs_set_string(INTKEYRING_PREFS
"encryption_method",
940 INTKEYRING_ENCRYPTION_METHOD
);
942 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_desired_iterations",
943 purple_request_fields_get_integer(fields
,
944 "pbkdf2_desired_iterations"));
946 if (passphrase
!= NULL
) {
947 if (!intkeyring_change_masterpw(passphrase
))
951 purple_prefs_set_bool(INTKEYRING_PREFS
"encrypt_passwords",
952 purple_request_fields_get_bool(fields
, "encrypt_passwords"));
954 purple_signal_emit(purple_keyring_get_handle(), "password-migration",
961 static PurplePluginInfo
*
962 plugin_query(GError
**error
)
964 const gchar
* const authors
[] = INTKEYRING_AUTHORS
;
966 return purple_plugin_info_new(
968 "name", INTKEYRING_NAME
,
969 "version", DISPLAY_VERSION
,
970 "category", N_("Keyring"),
971 "summary", "Internal Keyring Plugin",
972 "description", INTKEYRING_DESCRIPTION
,
974 "website", PURPLE_WEBSITE
,
975 "abi-version", PURPLE_ABI_VERSION
,
976 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
982 plugin_load(PurplePlugin
*plugin
, GError
**error
)
984 purple_prefs_add_none("/plugins/keyrings");
985 purple_prefs_add_none("/plugins/keyrings/internal");
986 purple_prefs_add_bool(INTKEYRING_PREFS
"encrypt_passwords", FALSE
);
987 purple_prefs_add_string(INTKEYRING_PREFS
"encryption_method",
988 INTKEYRING_ENCRYPTION_METHOD
);
989 purple_prefs_add_int(INTKEYRING_PREFS
"pbkdf2_desired_iterations",
990 INTKEYRING_PBKDF2_ITERATIONS
);
991 purple_prefs_add_int(INTKEYRING_PREFS
"pbkdf2_iterations",
992 INTKEYRING_PBKDF2_ITERATIONS
);
993 purple_prefs_add_string(INTKEYRING_PREFS
"pbkdf2_salt", "");
994 purple_prefs_add_string(INTKEYRING_PREFS
"key_verifier", "");
996 keyring_handler
= purple_keyring_new();
998 purple_keyring_set_name(keyring_handler
, _(INTKEYRING_NAME
));
999 purple_keyring_set_id(keyring_handler
, INTKEYRING_ID
);
1000 purple_keyring_set_read_password(keyring_handler
,
1002 purple_keyring_set_save_password(keyring_handler
,
1004 purple_keyring_set_close_keyring(keyring_handler
,
1006 purple_keyring_set_import_password(keyring_handler
,
1007 intkeyring_import_password
);
1008 purple_keyring_set_export_password(keyring_handler
,
1009 intkeyring_export_password
);
1010 purple_keyring_set_read_settings(keyring_handler
,
1011 intkeyring_read_settings
);
1012 purple_keyring_set_apply_settings(keyring_handler
,
1013 intkeyring_apply_settings
);
1015 purple_keyring_register(keyring_handler
);
1021 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
1023 if (purple_keyring_get_inuse() == keyring_handler
) {
1024 g_set_error(error
, INTKEYRING_DOMAIN
, 0, "The keyring is currently "
1026 purple_debug_warning("keyring-internal",
1027 "keyring in use, cannot unload\n");
1033 purple_keyring_unregister(keyring_handler
);
1034 purple_keyring_free(keyring_handler
);
1035 keyring_handler
= NULL
;
1037 if (intkeyring_key
!= NULL
) {
1038 purple_debug_warning("keyring-internal", "Master key should be "
1039 "cleaned up at this point\n");
1040 intkeyring_buff_free(intkeyring_key
);
1041 intkeyring_key
= NULL
;
1047 PURPLE_PLUGIN_INIT(internal_keyring
, plugin_query
, plugin_load
, plugin_unload
);