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
30 #include <nettle/aes.h>
31 #include <nettle/cbc.h>
32 #include <nettle/pbkdf2.h>
34 #define INTKEYRING_NAME N_("Internal keyring")
35 #define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \
36 "storage behaviour for libpurple.")
37 #define INTKEYRING_AUTHORS { "Tomek Wasilczyk <twasilczyk@pidgin.im>",NULL }
38 #define INTKEYRING_ID PURPLE_DEFAULT_KEYRING
39 #define INTKEYRING_DOMAIN (g_quark_from_static_string(INTKEYRING_ID))
41 #define INTKEYRING_VERIFY_STR "[verification-string]"
42 #define INTKEYRING_PBKDF2_ITERATIONS 10000
43 #define INTKEYRING_PBKDF2_ITERATIONS_MIN 1000
44 #define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000
45 #define INTKEYRING_KEY_LEN AES256_KEY_SIZE
46 #define INTKEYRING_ENCRYPT_BUFF_LEN 1000
47 #define INTKEYRING_ENCRYPTED_MIN_LEN 50
48 #define INTKEYRING_ENCRYPTION_METHOD "pbkdf2-sha256-aes256"
50 #define INTKEYRING_PREFS "/plugins/keyrings/internal/"
52 /* win32 build defines such macro to override read() routine */
59 INTKEYRING_REQUEST_READ
,
60 INTKEYRING_REQUEST_SAVE
62 PurpleAccount
*account
;
66 PurpleKeyringReadCallback read
;
67 PurpleKeyringSaveCallback save
;
78 static intkeyring_buff_t
*intkeyring_key
;
79 static GHashTable
*intkeyring_passwords
= NULL
;
80 static GHashTable
*intkeyring_ciphertexts
= NULL
;
82 static gboolean intkeyring_opened
= FALSE
;
83 static gboolean intkeyring_unlocked
= FALSE
;
85 static GList
*intkeyring_pending_requests
= NULL
;
86 static void *intkeyring_masterpw_uirequest
= NULL
;
88 static PurpleKeyring
*keyring_handler
= NULL
;
91 intkeyring_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
94 intkeyring_save(PurpleAccount
*account
, const gchar
*password
,
95 PurpleKeyringSaveCallback cb
, gpointer data
);
97 intkeyring_reencrypt_passwords(void);
99 intkeyring_unlock(const gchar
*message
);
102 intkeyring_request_free(intkeyring_request
*req
)
104 g_return_if_fail(req
!= NULL
);
106 purple_str_wipe(req
->password
);
110 static intkeyring_buff_t
*
111 intkeyring_buff_new(guchar
*data
, size_t len
)
113 intkeyring_buff_t
*ret
= g_new(intkeyring_buff_t
, 1);
122 intkeyring_buff_free(intkeyring_buff_t
*buff
)
127 memset(buff
->data
, 0, buff
->len
);
132 static intkeyring_buff_t
*
133 intkeyring_buff_from_base64(const gchar
*base64
)
138 data
= g_base64_decode(base64
, &len
);
140 return intkeyring_buff_new(data
, len
);
143 /************************************************************************/
144 /* Generic encryption stuff */
145 /************************************************************************/
147 static intkeyring_buff_t
*
148 intkeyring_derive_key(const gchar
*passphrase
, intkeyring_buff_t
*salt
)
150 intkeyring_buff_t
*ret
;
152 g_return_val_if_fail(passphrase
!= NULL
, NULL
);
154 ret
= intkeyring_buff_new(g_new(guchar
, INTKEYRING_KEY_LEN
),
157 pbkdf2_hmac_sha256(strlen(passphrase
), (const uint8_t *)passphrase
,
158 purple_prefs_get_int(INTKEYRING_PREFS
"pbkdf2_iterations"),
159 salt
->len
, salt
->data
, ret
->len
, ret
->data
);
164 static intkeyring_buff_t
*
165 intkeyring_gen_salt(size_t len
)
167 intkeyring_buff_t
*ret
;
170 g_return_val_if_fail(len
> 0, NULL
);
172 ret
= intkeyring_buff_new(g_new(guchar
, len
), len
);
174 while (filled
< len
) {
175 guint32 r
= g_random_int();
178 for (i
= 0; i
< 4; i
++) {
179 ret
->data
[filled
++] = r
& 0xFF;
190 * Encrypts a plaintext using the specified key.
192 * Random IV will be generated and stored with ciphertext.
195 * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++
196 * [ control string ] ++ [ pkcs7 padding ] )
198 * IV: Random, 128bit IV.
199 * plaintext: The plaintext.
200 * min length padding: The padding used to hide the rough length of short
201 * plaintexts, may have a length of 0.
202 * control string: Constant string, verifies corectness of decryption.
203 * pkcs7 padding: The padding used to determine total length of encrypted
204 * content (also provides some verification).
206 * @param key The AES key.
207 * @param str The NUL-terminated plaintext.
208 * @return The ciphertext with IV, encoded as base64. Must be g_free'd.
211 intkeyring_encrypt(intkeyring_buff_t
*key
, const gchar
*str
)
213 struct CBC_CTX(struct aes256_ctx
, AES_BLOCK_SIZE
) ctx
;
214 intkeyring_buff_t
*iv
;
215 guchar plaintext
[INTKEYRING_ENCRYPT_BUFF_LEN
];
216 size_t plaintext_len
, text_len
, verify_len
;
218 guchar encrypted_raw
[INTKEYRING_ENCRYPT_BUFF_LEN
];
219 ssize_t encrypted_size
;
221 g_return_val_if_fail(key
!= NULL
, NULL
);
222 g_return_val_if_fail(str
!= NULL
, NULL
);
224 text_len
= strlen(str
);
225 verify_len
= strlen(INTKEYRING_VERIFY_STR
);
226 plaintext_len
= INTKEYRING_ENCRYPTED_MIN_LEN
;
227 if (plaintext_len
< text_len
)
228 plaintext_len
= text_len
;
230 g_return_val_if_fail(plaintext_len
+ verify_len
<= sizeof(plaintext
),
233 memset(plaintext
, 0, plaintext_len
);
234 memcpy(plaintext
, str
, text_len
);
235 memcpy(plaintext
+ plaintext_len
, INTKEYRING_VERIFY_STR
, verify_len
);
236 plaintext_len
+= verify_len
;
239 padding_len
= AES_BLOCK_SIZE
- (plaintext_len
% AES_BLOCK_SIZE
);
241 if (plaintext_len
+ padding_len
> INTKEYRING_ENCRYPT_BUFF_LEN
) {
242 purple_debug_error("keyring-internal",
243 "Internal keyring encrypt buffer too small");
247 memset(plaintext
+ plaintext_len
, padding_len
, padding_len
);
248 plaintext_len
+= padding_len
;
251 iv
= intkeyring_gen_salt(AES_BLOCK_SIZE
);
252 g_return_val_if_fail(iv
!= NULL
, NULL
);
253 aes256_set_encrypt_key(&ctx
.ctx
, key
->data
);
254 CBC_SET_IV(&ctx
, iv
->data
);
256 memcpy(encrypted_raw
, iv
->data
, iv
->len
);
258 CBC_ENCRYPT(&ctx
, aes256_encrypt
, plaintext_len
,
259 encrypted_raw
+ iv
->len
, plaintext
);
260 encrypted_size
= plaintext_len
;
261 encrypted_size
+= iv
->len
;
263 memset(plaintext
, 0, plaintext_len
);
264 intkeyring_buff_free(iv
);
266 if (encrypted_size
< 0)
269 return g_base64_encode(encrypted_raw
, encrypted_size
);
274 intkeyring_decrypt(intkeyring_buff_t
*key
, const gchar
*str
)
276 struct CBC_CTX(struct aes256_ctx
, AES_BLOCK_SIZE
) ctx
;
277 guchar
*encrypted_raw
;
278 gsize encrypted_size
;
279 size_t iv_len
, verify_len
, text_len
;
280 guchar plaintext
[INTKEYRING_ENCRYPT_BUFF_LEN
];
281 const gchar
*verify_str
= NULL
;
282 size_t plaintext_len
;
287 g_return_val_if_fail(key
!= NULL
, NULL
);
288 g_return_val_if_fail(str
!= NULL
, NULL
);
290 encrypted_raw
= g_base64_decode(str
, &encrypted_size
);
291 g_return_val_if_fail(encrypted_raw
!= NULL
, NULL
);
293 iv_len
= AES_BLOCK_SIZE
;
294 if (encrypted_size
< iv_len
) {
295 g_free(encrypted_raw
);
300 aes256_set_decrypt_key(&ctx
.ctx
, key
->data
);
301 CBC_SET_IV(&ctx
, encrypted_raw
);
302 CBC_DECRYPT(&ctx
, aes256_decrypt
, encrypted_size
- iv_len
,
303 plaintext
, encrypted_raw
+ iv_len
);
304 plaintext_len
= encrypted_size
- iv_len
;
305 g_free(encrypted_raw
);
308 padding_len
= plaintext
[plaintext_len
- 1];
309 if (padding_len
== 0 || padding_len
> AES_BLOCK_SIZE
||
310 padding_len
> plaintext_len
) {
311 purple_debug_warning("internal-keyring",
312 "Invalid padding length: %d (total %" G_GSIZE_FORMAT
313 ") - most probably, the key was invalid\n",
314 padding_len
, plaintext_len
);
318 plaintext_len
-= padding_len
;
319 for (i
= 0; i
< padding_len
; ++i
) {
320 if (plaintext
[plaintext_len
+ i
] != padding_len
) {
321 purple_debug_warning("internal-keyring",
322 "Padding doesn't match at pos %d (found %02x, "
324 "most probably, the key was invalid\n",
325 i
, plaintext
[plaintext_len
+ i
], padding_len
);
330 memset(plaintext
+ plaintext_len
, 0, padding_len
);
333 verify_len
= strlen(INTKEYRING_VERIFY_STR
);
334 /* Don't remove the len > 0 check! */
335 if (plaintext_len
> 0 && (gsize
)plaintext_len
> verify_len
&&
336 plaintext
[plaintext_len
] == '\0')
338 verify_str
= (gchar
*)plaintext
+ plaintext_len
- verify_len
;
341 if (g_strcmp0(verify_str
, INTKEYRING_VERIFY_STR
) != 0) {
342 purple_debug_warning("keyring-internal",
343 "Verification failed on decryption\n");
344 memset(plaintext
, 0, sizeof(plaintext
));
347 g_assert(plaintext_len
> 0);
349 text_len
= plaintext_len
- verify_len
;
350 ret
= g_new(gchar
, text_len
+ 1);
351 memcpy(ret
, plaintext
, text_len
);
352 memset(plaintext
, 0, plaintext_len
);
353 ret
[text_len
] = '\0';
358 /************************************************************************/
359 /* Password encryption */
360 /************************************************************************/
363 intkeyring_change_masterpw(const gchar
*new_password
)
365 intkeyring_buff_t
*salt
, *key
;
366 gchar
*verifier
= NULL
, *salt_b64
= NULL
;
368 gboolean succ
= TRUE
;;
370 g_return_val_if_fail(intkeyring_unlocked
, FALSE
);
372 old_iter
= purple_prefs_get_int(INTKEYRING_PREFS
"pbkdf2_iterations");
373 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_iterations",
374 purple_prefs_get_int(INTKEYRING_PREFS
375 "pbkdf2_desired_iterations"));
377 salt
= intkeyring_gen_salt(32);
378 key
= intkeyring_derive_key(new_password
, salt
);
380 if (salt
&& key
&& key
->len
== INTKEYRING_KEY_LEN
) {
381 /* In fact, verify str will be concatenated twice before
382 * encryption (it's used as a suffix in encryption routine),
383 * but it's not a problem.
385 verifier
= intkeyring_encrypt(key
, INTKEYRING_VERIFY_STR
);
386 salt_b64
= g_base64_encode(salt
->data
, salt
->len
);
389 if (!verifier
|| !salt_b64
) {
390 purple_debug_error("keyring-internal", "Failed to change "
391 "master password\n");
393 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_iterations",
396 purple_prefs_set_string(INTKEYRING_PREFS
"pbkdf2_salt",
398 purple_prefs_set_string(INTKEYRING_PREFS
"key_verifier",
401 intkeyring_buff_free(intkeyring_key
);
402 intkeyring_key
= key
;
405 intkeyring_reencrypt_passwords();
407 purple_signal_emit(purple_keyring_get_handle(),
408 "password-migration", NULL
);
413 intkeyring_buff_free(salt
);
414 intkeyring_buff_free(key
);
420 intkeyring_process_queue(void)
422 GList
*requests
, *it
;
423 gboolean open
= intkeyring_unlocked
;
425 requests
= g_list_first(intkeyring_pending_requests
);
426 intkeyring_pending_requests
= NULL
;
428 for (it
= requests
; it
!= NULL
; it
= g_list_next(it
)) {
429 intkeyring_request
*req
= it
->data
;
431 if (open
&& req
->type
== INTKEYRING_REQUEST_READ
) {
432 intkeyring_read(req
->account
, req
->cb
.read
,
434 } else if (open
&& req
->type
== INTKEYRING_REQUEST_SAVE
) {
435 intkeyring_save(req
->account
, req
->password
,
436 req
->cb
.save
, req
->cb_data
);
438 g_assert_not_reached();
439 else if (req
->cb
.read
!= NULL
/* || req->cb.write != NULL */ ) {
440 GError
*error
= g_error_new_literal(PURPLE_KEYRING_ERROR
,
441 PURPLE_KEYRING_ERROR_CANCELLED
,
442 _("Operation cancelled."));
443 if (req
->type
== INTKEYRING_REQUEST_READ
) {
444 req
->cb
.read(req
->account
, NULL
, error
,
446 } else if (req
->type
== INTKEYRING_REQUEST_SAVE
)
447 req
->cb
.save(req
->account
, error
, req
->cb_data
);
449 g_assert_not_reached();
453 intkeyring_request_free(req
);
455 g_list_free(requests
);
459 intkeyring_decrypt_password(PurpleAccount
*account
, const gchar
*ciphertext
)
463 plaintext
= intkeyring_decrypt(intkeyring_key
, ciphertext
);
464 if (plaintext
== NULL
) {
465 purple_debug_warning("keyring-internal",
466 "Failed to decrypt a password\n");
470 g_hash_table_replace(intkeyring_passwords
, account
, plaintext
);
474 intkeyring_encrypt_password_if_needed(PurpleAccount
*account
)
476 const gchar
*plaintext
;
479 if (intkeyring_key
== NULL
) {
480 g_hash_table_remove(intkeyring_ciphertexts
, account
);
484 ciphertext
= g_hash_table_lookup(intkeyring_ciphertexts
, account
);
485 if (ciphertext
!= NULL
)
488 plaintext
= g_hash_table_lookup(intkeyring_passwords
, account
);
489 if (plaintext
== NULL
)
492 ciphertext
= intkeyring_encrypt(intkeyring_key
, plaintext
);
493 g_return_if_fail(ciphertext
!= NULL
);
495 g_hash_table_replace(intkeyring_ciphertexts
, account
, ciphertext
);
499 intkeyring_encrypt_passwords_if_needed_it(gpointer account
, gpointer plaintext
,
502 intkeyring_encrypt_password_if_needed(account
);
506 intkeyring_reencrypt_passwords(void)
508 g_hash_table_remove_all(intkeyring_ciphertexts
);
509 g_hash_table_foreach(intkeyring_passwords
,
510 intkeyring_encrypt_passwords_if_needed_it
, NULL
);
514 intkeyring_unlock_decrypt(gpointer account
, gpointer ciphertext
,
517 intkeyring_decrypt_password(account
, ciphertext
);
520 /************************************************************************/
521 /* Opening and unlocking keyring */
522 /************************************************************************/
525 intkeyring_unlock_ok(gpointer _unused
,
526 PurpleRequestFields
*fields
)
528 const gchar
*masterpw
;
530 intkeyring_buff_t
*salt
, *key
;
532 intkeyring_masterpw_uirequest
= NULL
;
534 if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS
535 "encryption_method"), INTKEYRING_ENCRYPTION_METHOD
) != 0)
537 purple_notify_error(NULL
,
538 _("Unlocking internal keyring"),
539 _("Selected encryption method is not supported."),
540 _("Most probably, your passwords were encrypted with "
541 "newer Pidgin/libpurple version, please update."),
546 masterpw
= purple_request_fields_get_string(fields
, "password");
548 if (masterpw
== NULL
|| masterpw
[0] == '\0') {
549 intkeyring_unlock(_("No password entered."));
553 salt
= intkeyring_buff_from_base64(purple_prefs_get_string(
554 INTKEYRING_PREFS
"pbkdf2_salt"));
555 key
= intkeyring_derive_key(masterpw
, salt
);
556 intkeyring_buff_free(salt
);
558 verifier
= intkeyring_decrypt(key
, purple_prefs_get_string(
559 INTKEYRING_PREFS
"key_verifier"));
561 if (g_strcmp0(verifier
, INTKEYRING_VERIFY_STR
) != 0) {
563 intkeyring_buff_free(key
);
564 intkeyring_unlock(_("Invalid master password entered, "
570 intkeyring_key
= key
;
571 intkeyring_unlocked
= TRUE
;
573 g_hash_table_foreach(intkeyring_ciphertexts
,
574 intkeyring_unlock_decrypt
, NULL
);
576 intkeyring_process_queue();
580 intkeyring_unlock_cancel(gpointer _unused
,
581 PurpleRequestFields
*fields
)
583 intkeyring_masterpw_uirequest
= NULL
;
584 intkeyring_process_queue();
588 intkeyring_unlock(const gchar
*message
)
590 PurpleRequestFields
*fields
;
591 PurpleRequestFieldGroup
*group
;
592 PurpleRequestField
*field
;
593 const gchar
*primary_msg
, *secondary_msg
= NULL
;
595 if (intkeyring_unlocked
|| intkeyring_masterpw_uirequest
!= NULL
)
598 if (!purple_prefs_get_bool(INTKEYRING_PREFS
"encrypt_passwords")) {
599 intkeyring_unlocked
= TRUE
;
600 intkeyring_process_queue();
604 fields
= purple_request_fields_new();
605 group
= purple_request_field_group_new(NULL
);
606 purple_request_fields_add_group(fields
, group
);
608 field
= purple_request_field_string_new("password",
609 _("Master password"), "", FALSE
);
610 purple_request_field_string_set_masked(field
, TRUE
);
611 purple_request_field_group_add_field(group
, field
);
613 primary_msg
= _("Please, enter master password");
615 secondary_msg
= primary_msg
;
616 primary_msg
= message
;
619 intkeyring_masterpw_uirequest
= purple_request_fields(NULL
,
620 _("Unlocking internal keyring"),
621 primary_msg
, secondary_msg
, fields
,
622 _("OK"), G_CALLBACK(intkeyring_unlock_ok
),
623 _("Cancel"), G_CALLBACK(intkeyring_unlock_cancel
),
628 intkeyring_open(void)
630 if (intkeyring_opened
)
632 intkeyring_opened
= TRUE
;
634 intkeyring_passwords
= g_hash_table_new_full(g_direct_hash
,
635 g_direct_equal
, NULL
, (GDestroyNotify
)purple_str_wipe
);
636 intkeyring_ciphertexts
= g_hash_table_new_full(g_direct_hash
,
637 g_direct_equal
, NULL
, g_free
);
640 /************************************************************************/
641 /* Keyring interface implementation */
642 /************************************************************************/
645 intkeyring_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
648 const char *password
;
653 if (!intkeyring_unlocked
&& g_hash_table_lookup(intkeyring_ciphertexts
,
656 intkeyring_request
*req
= g_new0(intkeyring_request
, 1);
658 req
->type
= INTKEYRING_REQUEST_READ
;
659 req
->account
= account
;
662 intkeyring_pending_requests
=
663 g_list_append(intkeyring_pending_requests
, req
);
665 intkeyring_unlock(NULL
);
669 password
= g_hash_table_lookup(intkeyring_passwords
, account
);
671 if (password
!= NULL
) {
672 purple_debug_misc("keyring-internal",
673 "Got password for account %s (%s).\n",
674 purple_account_get_username(account
),
675 purple_account_get_protocol_id(account
));
677 cb(account
, password
, NULL
, data
);
679 if (purple_debug_is_verbose()) {
680 purple_debug_misc("keyring-internal",
681 "No password for account %s (%s).\n",
682 purple_account_get_username(account
),
683 purple_account_get_protocol_id(account
));
685 error
= g_error_new_literal(PURPLE_KEYRING_ERROR
,
686 PURPLE_KEYRING_ERROR_NOPASSWORD
,
687 _("Password not found."));
689 cb(account
, NULL
, error
, data
);
695 intkeyring_save(PurpleAccount
*account
, const gchar
*password
,
696 PurpleKeyringSaveCallback cb
, gpointer data
)
702 if (!intkeyring_unlocked
) {
703 intkeyring_request
*req
;
705 if (password
== NULL
) {
706 g_hash_table_remove(intkeyring_ciphertexts
, account
);
707 g_hash_table_remove(intkeyring_passwords
, account
);
709 cb(account
, NULL
, data
);
713 req
= g_new0(intkeyring_request
, 1);
714 req
->type
= INTKEYRING_REQUEST_SAVE
;
715 req
->account
= account
;
716 req
->password
= g_strdup(password
);
719 intkeyring_pending_requests
=
720 g_list_append(intkeyring_pending_requests
, req
);
722 intkeyring_unlock(NULL
);
726 g_hash_table_remove(intkeyring_ciphertexts
, account
);
728 old_password
= g_hash_table_lookup(intkeyring_passwords
, account
);
730 if (password
== NULL
)
731 g_hash_table_remove(intkeyring_passwords
, account
);
733 g_hash_table_replace(intkeyring_passwords
, account
,
737 intkeyring_encrypt_password_if_needed(account
);
739 if (!(password
== NULL
&& old_password
== NULL
)) {
740 purple_debug_misc("keyring-internal",
741 "Password %s for account %s (%s).\n",
742 (password
== NULL
? "removed" : (old_password
== NULL
?
743 "saved" : "updated")),
744 purple_account_get_username(account
),
745 purple_account_get_protocol_id(account
));
746 } else if (purple_debug_is_verbose()) {
747 purple_debug_misc("keyring-internal",
748 "Password for account %s (%s) was already removed.\n",
749 purple_account_get_username(account
),
750 purple_account_get_protocol_id(account
));
754 cb(account
, NULL
, data
);
758 intkeyring_close(void)
760 if (!intkeyring_opened
)
762 intkeyring_opened
= FALSE
;
763 intkeyring_unlocked
= FALSE
;
765 if (intkeyring_masterpw_uirequest
) {
766 purple_request_close(PURPLE_REQUEST_FIELDS
,
767 intkeyring_masterpw_uirequest
);
769 g_warn_if_fail(intkeyring_masterpw_uirequest
== NULL
);
770 g_warn_if_fail(intkeyring_pending_requests
== NULL
);
772 intkeyring_buff_free(intkeyring_key
);
773 intkeyring_key
= NULL
;
774 g_hash_table_destroy(intkeyring_passwords
);
775 intkeyring_passwords
= NULL
;
776 g_hash_table_destroy(intkeyring_ciphertexts
);
777 intkeyring_ciphertexts
= NULL
;
781 intkeyring_import_password(PurpleAccount
*account
, const char *mode
,
782 const char *data
, GError
**error
)
784 g_return_val_if_fail(account
!= NULL
, FALSE
);
785 g_return_val_if_fail(data
!= NULL
, FALSE
);
792 if (g_strcmp0(mode
, "cleartext") == 0) {
793 g_hash_table_replace(intkeyring_passwords
, account
,
796 } else if (g_strcmp0(mode
, "ciphertext") == 0) {
797 if (intkeyring_unlocked
)
798 intkeyring_decrypt_password(account
, data
);
800 g_hash_table_replace(intkeyring_ciphertexts
, account
,
805 g_set_error_literal(error
, PURPLE_KEYRING_ERROR
,
806 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
807 _("Invalid password storage mode."));
813 intkeyring_export_password(PurpleAccount
*account
, const char **mode
,
814 char **data
, GError
**error
, GDestroyNotify
*destroy
)
816 gchar
*ciphertext
= NULL
;
819 if (!purple_prefs_get_bool(INTKEYRING_PREFS
"encrypt_passwords")) {
820 gchar
*cleartext
= g_hash_table_lookup(intkeyring_passwords
,
823 if (cleartext
== NULL
)
827 *data
= g_strdup(cleartext
);
828 *destroy
= (GDestroyNotify
)purple_str_wipe
;
832 ciphertext
= g_strdup(g_hash_table_lookup(intkeyring_ciphertexts
,
835 if (ciphertext
== NULL
&& intkeyring_unlocked
) {
836 gchar
*plaintext
= g_hash_table_lookup(intkeyring_passwords
,
839 if (plaintext
== NULL
)
842 purple_debug_warning("keyring-internal", "Encrypted password "
843 "is missing at export (it shouldn't happen)\n");
844 ciphertext
= intkeyring_encrypt(intkeyring_key
, plaintext
);
847 if (ciphertext
== NULL
)
850 *mode
= "ciphertext";
852 *destroy
= (GDestroyNotify
)g_free
;
856 static PurpleRequestFields
*
857 intkeyring_read_settings(void)
859 PurpleRequestFields
*fields
;
860 PurpleRequestFieldGroup
*group
;
861 PurpleRequestField
*field
;
863 fields
= purple_request_fields_new();
864 group
= purple_request_field_group_new(NULL
);
865 purple_request_fields_add_group(fields
, group
);
867 field
= purple_request_field_bool_new("encrypt_passwords",
868 _("Encrypt passwords"), purple_prefs_get_bool(
869 INTKEYRING_PREFS
"encrypt_passwords"));
870 purple_request_field_group_add_field(group
, field
);
872 group
= purple_request_field_group_new(_("Master password"));
873 purple_request_fields_add_group(fields
, group
);
875 field
= purple_request_field_string_new("passphrase1",
876 _("New passphrase:"), "", FALSE
);
877 purple_request_field_string_set_masked(field
, TRUE
);
878 purple_request_field_group_add_field(group
, field
);
880 field
= purple_request_field_string_new("passphrase2",
881 _("New passphrase (again):"), "", FALSE
);
882 purple_request_field_string_set_masked(field
, TRUE
);
883 purple_request_field_group_add_field(group
, field
);
885 group
= purple_request_field_group_new(_("Advanced settings"));
886 purple_request_fields_add_group(fields
, group
);
888 field
= purple_request_field_int_new("pbkdf2_desired_iterations",
889 _("Number of PBKDF2 iterations:"), purple_prefs_get_int(
890 INTKEYRING_PREFS
"pbkdf2_desired_iterations"),
891 INTKEYRING_PBKDF2_ITERATIONS_MIN
,
892 INTKEYRING_PBKDF2_ITERATIONS_MAX
);
893 purple_request_field_group_add_field(group
, field
);
899 intkeyring_apply_settings(void *notify_handle
,
900 PurpleRequestFields
*fields
)
902 const gchar
*passphrase
, *passphrase2
;
904 intkeyring_unlock(_("You have to unlock the keyring first."));
905 if (!intkeyring_unlocked
)
908 passphrase
= purple_request_fields_get_string(fields
, "passphrase1");
909 if (g_strcmp0(passphrase
, "") == 0)
911 passphrase2
= purple_request_fields_get_string(fields
, "passphrase2");
912 if (g_strcmp0(passphrase2
, "") == 0)
915 if (g_strcmp0(passphrase
, passphrase2
) != 0) {
916 purple_notify_error(notify_handle
,
917 _("Internal keyring settings"),
918 _("Passphrases do not match"), NULL
, NULL
);
922 if (purple_request_fields_get_bool(fields
, "encrypt_passwords") &&
923 !passphrase
&& !intkeyring_key
)
925 purple_notify_error(notify_handle
,
926 _("Internal keyring settings"),
927 _("You have to set up a Master password, if you want "
928 "to enable encryption"), NULL
, NULL
);
932 if (!purple_request_fields_get_bool(fields
, "encrypt_passwords") &&
935 purple_notify_error(notify_handle
,
936 _("Internal keyring settings"),
937 _("You don't need any master password, if you won't "
938 "enable passwords encryption"), NULL
, NULL
);
942 purple_prefs_set_string(INTKEYRING_PREFS
"encryption_method",
943 INTKEYRING_ENCRYPTION_METHOD
);
945 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_desired_iterations",
946 purple_request_fields_get_integer(fields
,
947 "pbkdf2_desired_iterations"));
949 if (passphrase
!= NULL
) {
950 if (!intkeyring_change_masterpw(passphrase
))
954 purple_prefs_set_bool(INTKEYRING_PREFS
"encrypt_passwords",
955 purple_request_fields_get_bool(fields
, "encrypt_passwords"));
957 purple_signal_emit(purple_keyring_get_handle(), "password-migration",
964 static PurplePluginInfo
*
965 plugin_query(GError
**error
)
967 const gchar
* const authors
[] = INTKEYRING_AUTHORS
;
969 return purple_plugin_info_new(
971 "name", INTKEYRING_NAME
,
972 "version", DISPLAY_VERSION
,
973 "category", N_("Keyring"),
974 "summary", "Internal Keyring Plugin",
975 "description", INTKEYRING_DESCRIPTION
,
977 "website", PURPLE_WEBSITE
,
978 "abi-version", PURPLE_ABI_VERSION
,
979 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
985 plugin_load(PurplePlugin
*plugin
, GError
**error
)
987 purple_prefs_add_none("/plugins/keyrings");
988 purple_prefs_add_none("/plugins/keyrings/internal");
989 purple_prefs_add_bool(INTKEYRING_PREFS
"encrypt_passwords", FALSE
);
990 purple_prefs_add_string(INTKEYRING_PREFS
"encryption_method",
991 INTKEYRING_ENCRYPTION_METHOD
);
992 purple_prefs_add_int(INTKEYRING_PREFS
"pbkdf2_desired_iterations",
993 INTKEYRING_PBKDF2_ITERATIONS
);
994 purple_prefs_add_int(INTKEYRING_PREFS
"pbkdf2_iterations",
995 INTKEYRING_PBKDF2_ITERATIONS
);
996 purple_prefs_add_string(INTKEYRING_PREFS
"pbkdf2_salt", "");
997 purple_prefs_add_string(INTKEYRING_PREFS
"key_verifier", "");
999 keyring_handler
= purple_keyring_new();
1001 purple_keyring_set_name(keyring_handler
, _(INTKEYRING_NAME
));
1002 purple_keyring_set_id(keyring_handler
, INTKEYRING_ID
);
1003 purple_keyring_set_read_password(keyring_handler
,
1005 purple_keyring_set_save_password(keyring_handler
,
1007 purple_keyring_set_close_keyring(keyring_handler
,
1009 purple_keyring_set_import_password(keyring_handler
,
1010 intkeyring_import_password
);
1011 purple_keyring_set_export_password(keyring_handler
,
1012 intkeyring_export_password
);
1013 purple_keyring_set_read_settings(keyring_handler
,
1014 intkeyring_read_settings
);
1015 purple_keyring_set_apply_settings(keyring_handler
,
1016 intkeyring_apply_settings
);
1018 purple_keyring_register(keyring_handler
);
1024 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
1026 if (purple_keyring_get_inuse() == keyring_handler
) {
1027 g_set_error(error
, INTKEYRING_DOMAIN
, 0, "The keyring is currently "
1029 purple_debug_warning("keyring-internal",
1030 "keyring in use, cannot unload\n");
1036 purple_keyring_unregister(keyring_handler
);
1037 purple_keyring_free(keyring_handler
);
1038 keyring_handler
= NULL
;
1040 if (intkeyring_key
!= NULL
) {
1041 purple_debug_warning("keyring-internal", "Master key should be "
1042 "cleaned up at this point\n");
1043 intkeyring_buff_free(intkeyring_key
);
1044 intkeyring_key
= NULL
;
1050 PURPLE_PLUGIN_INIT(internal_keyring
, plugin_query
, plugin_load
, plugin_unload
);