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 <nettle/aes.h>
35 #include <nettle/cbc.h>
36 #include <nettle/pbkdf2.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 AES256_KEY_SIZE
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
= g_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 intkeyring_buff_t
*ret
;
156 g_return_val_if_fail(passphrase
!= NULL
, NULL
);
158 ret
= intkeyring_buff_new(g_new(guchar
, INTKEYRING_KEY_LEN
),
161 pbkdf2_hmac_sha256(strlen(passphrase
), (const uint8_t *)passphrase
,
162 purple_prefs_get_int(INTKEYRING_PREFS
"pbkdf2_iterations"),
163 salt
->len
, salt
->data
, ret
->len
, ret
->data
);
168 static intkeyring_buff_t
*
169 intkeyring_gen_salt(size_t len
)
171 intkeyring_buff_t
*ret
;
174 g_return_val_if_fail(len
> 0, NULL
);
176 ret
= intkeyring_buff_new(g_new(guchar
, len
), len
);
178 while (filled
< len
) {
179 guint32 r
= g_random_int();
182 for (i
= 0; i
< 4; i
++) {
183 ret
->data
[filled
++] = r
& 0xFF;
194 * Encrypts a plaintext using the specified key.
196 * Random IV will be generated and stored with ciphertext.
199 * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++
200 * [ control string ] ++ [ pkcs7 padding ] )
202 * IV: Random, 128bit IV.
203 * plaintext: The plaintext.
204 * min length padding: The padding used to hide the rough length of short
205 * plaintexts, may have a length of 0.
206 * control string: Constant string, verifies corectness of decryption.
207 * pkcs7 padding: The padding used to determine total length of encrypted
208 * content (also provides some verification).
210 * @param key The AES key.
211 * @param str The NUL-terminated plaintext.
212 * @return The ciphertext with IV, encoded as base64. Must be g_free'd.
215 intkeyring_encrypt(intkeyring_buff_t
*key
, const gchar
*str
)
217 struct CBC_CTX(struct aes256_ctx
, AES_BLOCK_SIZE
) ctx
;
218 intkeyring_buff_t
*iv
;
219 guchar plaintext
[INTKEYRING_ENCRYPT_BUFF_LEN
];
220 size_t plaintext_len
, text_len
, verify_len
;
222 guchar encrypted_raw
[INTKEYRING_ENCRYPT_BUFF_LEN
];
223 ssize_t encrypted_size
;
225 g_return_val_if_fail(key
!= NULL
, NULL
);
226 g_return_val_if_fail(str
!= NULL
, NULL
);
228 text_len
= strlen(str
);
229 verify_len
= strlen(INTKEYRING_VERIFY_STR
);
230 plaintext_len
= INTKEYRING_ENCRYPTED_MIN_LEN
;
231 if (plaintext_len
< text_len
)
232 plaintext_len
= text_len
;
234 g_return_val_if_fail(plaintext_len
+ verify_len
<= sizeof(plaintext
),
237 memset(plaintext
, 0, plaintext_len
);
238 memcpy(plaintext
, str
, text_len
);
239 memcpy(plaintext
+ plaintext_len
, INTKEYRING_VERIFY_STR
, verify_len
);
240 plaintext_len
+= verify_len
;
243 padding_len
= AES_BLOCK_SIZE
- (plaintext_len
% AES_BLOCK_SIZE
);
245 if (plaintext_len
+ padding_len
> INTKEYRING_ENCRYPT_BUFF_LEN
) {
246 purple_debug_error("keyring-internal",
247 "Internal keyring encrypt buffer too small");
251 memset(plaintext
+ plaintext_len
, padding_len
, padding_len
);
252 plaintext_len
+= padding_len
;
255 iv
= intkeyring_gen_salt(AES_BLOCK_SIZE
);
256 g_return_val_if_fail(iv
!= NULL
, NULL
);
257 aes256_set_encrypt_key(&ctx
.ctx
, key
->data
);
258 CBC_SET_IV(&ctx
, iv
->data
);
260 memcpy(encrypted_raw
, iv
->data
, iv
->len
);
262 CBC_ENCRYPT(&ctx
, aes256_encrypt
, plaintext_len
,
263 encrypted_raw
+ iv
->len
, plaintext
);
264 encrypted_size
= plaintext_len
;
265 encrypted_size
+= iv
->len
;
267 memset(plaintext
, 0, plaintext_len
);
268 intkeyring_buff_free(iv
);
270 if (encrypted_size
< 0)
273 return g_base64_encode(encrypted_raw
, encrypted_size
);
278 intkeyring_decrypt(intkeyring_buff_t
*key
, const gchar
*str
)
280 struct CBC_CTX(struct aes256_ctx
, AES_BLOCK_SIZE
) ctx
;
281 guchar
*encrypted_raw
;
282 gsize encrypted_size
;
283 size_t iv_len
, verify_len
, text_len
;
284 guchar plaintext
[INTKEYRING_ENCRYPT_BUFF_LEN
];
285 const gchar
*verify_str
= NULL
;
286 size_t plaintext_len
;
291 g_return_val_if_fail(key
!= NULL
, NULL
);
292 g_return_val_if_fail(str
!= NULL
, NULL
);
294 encrypted_raw
= g_base64_decode(str
, &encrypted_size
);
295 g_return_val_if_fail(encrypted_raw
!= NULL
, NULL
);
297 iv_len
= AES_BLOCK_SIZE
;
298 if (encrypted_size
< iv_len
) {
299 g_free(encrypted_raw
);
304 aes256_set_decrypt_key(&ctx
.ctx
, key
->data
);
305 CBC_SET_IV(&ctx
, encrypted_raw
);
306 CBC_DECRYPT(&ctx
, aes256_decrypt
, encrypted_size
- iv_len
,
307 plaintext
, encrypted_raw
+ iv_len
);
308 plaintext_len
= encrypted_size
- iv_len
;
309 g_free(encrypted_raw
);
312 padding_len
= plaintext
[plaintext_len
- 1];
313 if (padding_len
== 0 || padding_len
> AES_BLOCK_SIZE
||
314 padding_len
> plaintext_len
) {
315 purple_debug_warning("internal-keyring",
316 "Invalid padding length: %d (total %" G_GSIZE_FORMAT
317 ") - most probably, the key was invalid\n",
318 padding_len
, plaintext_len
);
322 plaintext_len
-= padding_len
;
323 for (i
= 0; i
< padding_len
; ++i
) {
324 if (plaintext
[plaintext_len
+ i
] != padding_len
) {
325 purple_debug_warning("internal-keyring",
326 "Padding doesn't match at pos %d (found %02x, "
328 "most probably, the key was invalid\n",
329 i
, plaintext
[plaintext_len
+ i
], padding_len
);
334 memset(plaintext
+ plaintext_len
, 0, padding_len
);
337 verify_len
= strlen(INTKEYRING_VERIFY_STR
);
338 /* Don't remove the len > 0 check! */
339 if (plaintext_len
> 0 && (gsize
)plaintext_len
> verify_len
&&
340 plaintext
[plaintext_len
] == '\0')
342 verify_str
= (gchar
*)plaintext
+ plaintext_len
- verify_len
;
345 if (g_strcmp0(verify_str
, INTKEYRING_VERIFY_STR
) != 0) {
346 purple_debug_warning("keyring-internal",
347 "Verification failed on decryption\n");
348 memset(plaintext
, 0, sizeof(plaintext
));
351 g_assert(plaintext_len
> 0);
353 text_len
= plaintext_len
- verify_len
;
354 ret
= g_new(gchar
, text_len
+ 1);
355 memcpy(ret
, plaintext
, text_len
);
356 memset(plaintext
, 0, plaintext_len
);
357 ret
[text_len
] = '\0';
362 /************************************************************************/
363 /* Password encryption */
364 /************************************************************************/
367 intkeyring_change_masterpw(const gchar
*new_password
)
369 intkeyring_buff_t
*salt
, *key
;
370 gchar
*verifier
= NULL
, *salt_b64
= NULL
;
372 gboolean succ
= TRUE
;;
374 g_return_val_if_fail(intkeyring_unlocked
, FALSE
);
376 old_iter
= purple_prefs_get_int(INTKEYRING_PREFS
"pbkdf2_iterations");
377 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_iterations",
378 purple_prefs_get_int(INTKEYRING_PREFS
379 "pbkdf2_desired_iterations"));
381 salt
= intkeyring_gen_salt(32);
382 key
= intkeyring_derive_key(new_password
, salt
);
384 if (salt
&& key
&& key
->len
== INTKEYRING_KEY_LEN
) {
385 /* In fact, verify str will be concatenated twice before
386 * encryption (it's used as a suffix in encryption routine),
387 * but it's not a problem.
389 verifier
= intkeyring_encrypt(key
, INTKEYRING_VERIFY_STR
);
390 salt_b64
= g_base64_encode(salt
->data
, salt
->len
);
393 if (!verifier
|| !salt_b64
) {
394 purple_debug_error("keyring-internal", "Failed to change "
395 "master password\n");
397 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_iterations",
400 purple_prefs_set_string(INTKEYRING_PREFS
"pbkdf2_salt",
402 purple_prefs_set_string(INTKEYRING_PREFS
"key_verifier",
405 intkeyring_buff_free(intkeyring_key
);
406 intkeyring_key
= key
;
409 intkeyring_reencrypt_passwords();
411 purple_signal_emit(purple_keyring_get_handle(),
412 "password-migration", NULL
);
417 intkeyring_buff_free(salt
);
418 intkeyring_buff_free(key
);
424 intkeyring_process_queue(void)
426 GList
*requests
, *it
;
427 gboolean open
= intkeyring_unlocked
;
429 requests
= g_list_first(intkeyring_pending_requests
);
430 intkeyring_pending_requests
= NULL
;
432 for (it
= requests
; it
!= NULL
; it
= g_list_next(it
)) {
433 intkeyring_request
*req
= it
->data
;
435 if (open
&& req
->type
== INTKEYRING_REQUEST_READ
) {
436 intkeyring_read(req
->account
, req
->cb
.read
,
438 } else if (open
&& req
->type
== INTKEYRING_REQUEST_SAVE
) {
439 intkeyring_save(req
->account
, req
->password
,
440 req
->cb
.save
, req
->cb_data
);
442 g_assert_not_reached();
443 else if (req
->cb
.read
!= NULL
/* || req->cb.write != NULL */ ) {
444 GError
*error
= g_error_new(PURPLE_KEYRING_ERROR
,
445 PURPLE_KEYRING_ERROR_CANCELLED
,
446 _("Operation cancelled."));
447 if (req
->type
== INTKEYRING_REQUEST_READ
) {
448 req
->cb
.read(req
->account
, NULL
, error
,
450 } else if (req
->type
== INTKEYRING_REQUEST_SAVE
)
451 req
->cb
.save(req
->account
, error
, req
->cb_data
);
453 g_assert_not_reached();
457 intkeyring_request_free(req
);
459 g_list_free(requests
);
463 intkeyring_decrypt_password(PurpleAccount
*account
, const gchar
*ciphertext
)
467 plaintext
= intkeyring_decrypt(intkeyring_key
, ciphertext
);
468 if (plaintext
== NULL
) {
469 purple_debug_warning("keyring-internal",
470 "Failed to decrypt a password\n");
474 g_hash_table_replace(intkeyring_passwords
, account
, plaintext
);
478 intkeyring_encrypt_password_if_needed(PurpleAccount
*account
)
480 const gchar
*plaintext
;
483 if (intkeyring_key
== NULL
) {
484 g_hash_table_remove(intkeyring_ciphertexts
, account
);
488 ciphertext
= g_hash_table_lookup(intkeyring_ciphertexts
, account
);
489 if (ciphertext
!= NULL
)
492 plaintext
= g_hash_table_lookup(intkeyring_passwords
, account
);
493 if (plaintext
== NULL
)
496 ciphertext
= intkeyring_encrypt(intkeyring_key
, plaintext
);
497 g_return_if_fail(ciphertext
!= NULL
);
499 g_hash_table_replace(intkeyring_ciphertexts
, account
, ciphertext
);
503 intkeyring_encrypt_passwords_if_needed_it(gpointer account
, gpointer plaintext
,
506 intkeyring_encrypt_password_if_needed(account
);
510 intkeyring_reencrypt_passwords(void)
512 g_hash_table_remove_all(intkeyring_ciphertexts
);
513 g_hash_table_foreach(intkeyring_passwords
,
514 intkeyring_encrypt_passwords_if_needed_it
, NULL
);
518 intkeyring_unlock_decrypt(gpointer account
, gpointer ciphertext
,
521 intkeyring_decrypt_password(account
, ciphertext
);
524 /************************************************************************/
525 /* Opening and unlocking keyring */
526 /************************************************************************/
529 intkeyring_unlock_ok(gpointer _unused
,
530 PurpleRequestFields
*fields
)
532 const gchar
*masterpw
;
534 intkeyring_buff_t
*salt
, *key
;
536 intkeyring_masterpw_uirequest
= NULL
;
538 if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS
539 "encryption_method"), INTKEYRING_ENCRYPTION_METHOD
) != 0)
541 purple_notify_error(NULL
,
542 _("Unlocking internal keyring"),
543 _("Selected encryption method is not supported."),
544 _("Most probably, your passwords were encrypted with "
545 "newer Pidgin/libpurple version, please update."),
550 masterpw
= purple_request_fields_get_string(fields
, "password");
552 if (masterpw
== NULL
|| masterpw
[0] == '\0') {
553 intkeyring_unlock(_("No password entered."));
557 salt
= intkeyring_buff_from_base64(purple_prefs_get_string(
558 INTKEYRING_PREFS
"pbkdf2_salt"));
559 key
= intkeyring_derive_key(masterpw
, salt
);
560 intkeyring_buff_free(salt
);
562 verifier
= intkeyring_decrypt(key
, purple_prefs_get_string(
563 INTKEYRING_PREFS
"key_verifier"));
565 if (g_strcmp0(verifier
, INTKEYRING_VERIFY_STR
) != 0) {
567 intkeyring_buff_free(key
);
568 intkeyring_unlock(_("Invalid master password entered, "
574 intkeyring_key
= key
;
575 intkeyring_unlocked
= TRUE
;
577 g_hash_table_foreach(intkeyring_ciphertexts
,
578 intkeyring_unlock_decrypt
, NULL
);
580 intkeyring_process_queue();
584 intkeyring_unlock_cancel(gpointer _unused
,
585 PurpleRequestFields
*fields
)
587 intkeyring_masterpw_uirequest
= NULL
;
588 intkeyring_process_queue();
592 intkeyring_unlock(const gchar
*message
)
594 PurpleRequestFields
*fields
;
595 PurpleRequestFieldGroup
*group
;
596 PurpleRequestField
*field
;
597 const gchar
*primary_msg
, *secondary_msg
= NULL
;
599 if (intkeyring_unlocked
|| intkeyring_masterpw_uirequest
!= NULL
)
602 if (!purple_prefs_get_bool(INTKEYRING_PREFS
"encrypt_passwords")) {
603 intkeyring_unlocked
= TRUE
;
604 intkeyring_process_queue();
608 fields
= purple_request_fields_new();
609 group
= purple_request_field_group_new(NULL
);
610 purple_request_fields_add_group(fields
, group
);
612 field
= purple_request_field_string_new("password",
613 _("Master password"), "", FALSE
);
614 purple_request_field_string_set_masked(field
, TRUE
);
615 purple_request_field_group_add_field(group
, field
);
617 primary_msg
= _("Please, enter master password");
619 secondary_msg
= primary_msg
;
620 primary_msg
= message
;
623 intkeyring_masterpw_uirequest
= purple_request_fields(NULL
,
624 _("Unlocking internal keyring"),
625 primary_msg
, secondary_msg
, fields
,
626 _("OK"), G_CALLBACK(intkeyring_unlock_ok
),
627 _("Cancel"), G_CALLBACK(intkeyring_unlock_cancel
),
632 intkeyring_open(void)
634 if (intkeyring_opened
)
636 intkeyring_opened
= TRUE
;
638 intkeyring_passwords
= g_hash_table_new_full(g_direct_hash
,
639 g_direct_equal
, NULL
, (GDestroyNotify
)purple_str_wipe
);
640 intkeyring_ciphertexts
= g_hash_table_new_full(g_direct_hash
,
641 g_direct_equal
, NULL
, g_free
);
644 /************************************************************************/
645 /* Keyring interface implementation */
646 /************************************************************************/
649 intkeyring_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
652 const char *password
;
657 if (!intkeyring_unlocked
&& g_hash_table_lookup(intkeyring_ciphertexts
,
660 intkeyring_request
*req
= g_new0(intkeyring_request
, 1);
662 req
->type
= INTKEYRING_REQUEST_READ
;
663 req
->account
= account
;
666 intkeyring_pending_requests
=
667 g_list_append(intkeyring_pending_requests
, req
);
669 intkeyring_unlock(NULL
);
673 password
= g_hash_table_lookup(intkeyring_passwords
, account
);
675 if (password
!= NULL
) {
676 purple_debug_misc("keyring-internal",
677 "Got password for account %s (%s).\n",
678 purple_account_get_username(account
),
679 purple_account_get_protocol_id(account
));
681 cb(account
, password
, NULL
, data
);
683 if (purple_debug_is_verbose()) {
684 purple_debug_misc("keyring-internal",
685 "No password for account %s (%s).\n",
686 purple_account_get_username(account
),
687 purple_account_get_protocol_id(account
));
689 error
= g_error_new(PURPLE_KEYRING_ERROR
,
690 PURPLE_KEYRING_ERROR_NOPASSWORD
,
691 _("Password not found."));
693 cb(account
, NULL
, error
, data
);
699 intkeyring_save(PurpleAccount
*account
, const gchar
*password
,
700 PurpleKeyringSaveCallback cb
, gpointer data
)
706 if (!intkeyring_unlocked
) {
707 intkeyring_request
*req
;
709 if (password
== NULL
) {
710 g_hash_table_remove(intkeyring_ciphertexts
, account
);
711 g_hash_table_remove(intkeyring_passwords
, account
);
713 cb(account
, NULL
, data
);
717 req
= g_new0(intkeyring_request
, 1);
718 req
->type
= INTKEYRING_REQUEST_SAVE
;
719 req
->account
= account
;
720 req
->password
= g_strdup(password
);
723 intkeyring_pending_requests
=
724 g_list_append(intkeyring_pending_requests
, req
);
726 intkeyring_unlock(NULL
);
730 g_hash_table_remove(intkeyring_ciphertexts
, account
);
732 old_password
= g_hash_table_lookup(intkeyring_passwords
, account
);
734 if (password
== NULL
)
735 g_hash_table_remove(intkeyring_passwords
, account
);
737 g_hash_table_replace(intkeyring_passwords
, account
,
741 intkeyring_encrypt_password_if_needed(account
);
743 if (!(password
== NULL
&& old_password
== NULL
)) {
744 purple_debug_misc("keyring-internal",
745 "Password %s for account %s (%s).\n",
746 (password
== NULL
? "removed" : (old_password
== NULL
?
747 "saved" : "updated")),
748 purple_account_get_username(account
),
749 purple_account_get_protocol_id(account
));
750 } else if (purple_debug_is_verbose()) {
751 purple_debug_misc("keyring-internal",
752 "Password for account %s (%s) was already removed.\n",
753 purple_account_get_username(account
),
754 purple_account_get_protocol_id(account
));
758 cb(account
, NULL
, data
);
762 intkeyring_close(void)
764 if (!intkeyring_opened
)
766 intkeyring_opened
= FALSE
;
767 intkeyring_unlocked
= FALSE
;
769 if (intkeyring_masterpw_uirequest
) {
770 purple_request_close(PURPLE_REQUEST_FIELDS
,
771 intkeyring_masterpw_uirequest
);
773 g_warn_if_fail(intkeyring_masterpw_uirequest
== NULL
);
774 g_warn_if_fail(intkeyring_pending_requests
== NULL
);
776 intkeyring_buff_free(intkeyring_key
);
777 intkeyring_key
= NULL
;
778 g_hash_table_destroy(intkeyring_passwords
);
779 intkeyring_passwords
= NULL
;
780 g_hash_table_destroy(intkeyring_ciphertexts
);
781 intkeyring_ciphertexts
= NULL
;
785 intkeyring_import_password(PurpleAccount
*account
, const char *mode
,
786 const char *data
, GError
**error
)
788 g_return_val_if_fail(account
!= NULL
, FALSE
);
789 g_return_val_if_fail(data
!= NULL
, FALSE
);
796 if (g_strcmp0(mode
, "cleartext") == 0) {
797 g_hash_table_replace(intkeyring_passwords
, account
,
800 } else if (g_strcmp0(mode
, "ciphertext") == 0) {
801 if (intkeyring_unlocked
)
802 intkeyring_decrypt_password(account
, data
);
804 g_hash_table_replace(intkeyring_ciphertexts
, account
,
810 *error
= g_error_new(PURPLE_KEYRING_ERROR
,
811 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
812 _("Invalid password storage mode."));
819 intkeyring_export_password(PurpleAccount
*account
, const char **mode
,
820 char **data
, GError
**error
, GDestroyNotify
*destroy
)
822 gchar
*ciphertext
= NULL
;
825 if (!purple_prefs_get_bool(INTKEYRING_PREFS
"encrypt_passwords")) {
826 gchar
*cleartext
= g_hash_table_lookup(intkeyring_passwords
,
829 if (cleartext
== NULL
)
833 *data
= g_strdup(cleartext
);
834 *destroy
= (GDestroyNotify
)purple_str_wipe
;
838 ciphertext
= g_strdup(g_hash_table_lookup(intkeyring_ciphertexts
,
841 if (ciphertext
== NULL
&& intkeyring_unlocked
) {
842 gchar
*plaintext
= g_hash_table_lookup(intkeyring_passwords
,
845 if (plaintext
== NULL
)
848 purple_debug_warning("keyring-internal", "Encrypted password "
849 "is missing at export (it shouldn't happen)\n");
850 ciphertext
= intkeyring_encrypt(intkeyring_key
, plaintext
);
853 if (ciphertext
== NULL
)
856 *mode
= "ciphertext";
858 *destroy
= (GDestroyNotify
)g_free
;
862 static PurpleRequestFields
*
863 intkeyring_read_settings(void)
865 PurpleRequestFields
*fields
;
866 PurpleRequestFieldGroup
*group
;
867 PurpleRequestField
*field
;
869 fields
= purple_request_fields_new();
870 group
= purple_request_field_group_new(NULL
);
871 purple_request_fields_add_group(fields
, group
);
873 field
= purple_request_field_bool_new("encrypt_passwords",
874 _("Encrypt passwords"), purple_prefs_get_bool(
875 INTKEYRING_PREFS
"encrypt_passwords"));
876 purple_request_field_group_add_field(group
, field
);
878 group
= purple_request_field_group_new(_("Master password"));
879 purple_request_fields_add_group(fields
, group
);
881 field
= purple_request_field_string_new("passphrase1",
882 _("New passphrase:"), "", FALSE
);
883 purple_request_field_string_set_masked(field
, TRUE
);
884 purple_request_field_group_add_field(group
, field
);
886 field
= purple_request_field_string_new("passphrase2",
887 _("New passphrase (again):"), "", FALSE
);
888 purple_request_field_string_set_masked(field
, TRUE
);
889 purple_request_field_group_add_field(group
, field
);
891 group
= purple_request_field_group_new(_("Advanced settings"));
892 purple_request_fields_add_group(fields
, group
);
894 field
= purple_request_field_int_new("pbkdf2_desired_iterations",
895 _("Number of PBKDF2 iterations:"), purple_prefs_get_int(
896 INTKEYRING_PREFS
"pbkdf2_desired_iterations"),
897 INTKEYRING_PBKDF2_ITERATIONS_MIN
,
898 INTKEYRING_PBKDF2_ITERATIONS_MAX
);
899 purple_request_field_group_add_field(group
, field
);
905 intkeyring_apply_settings(void *notify_handle
,
906 PurpleRequestFields
*fields
)
908 const gchar
*passphrase
, *passphrase2
;
910 intkeyring_unlock(_("You have to unlock the keyring first."));
911 if (!intkeyring_unlocked
)
914 passphrase
= purple_request_fields_get_string(fields
, "passphrase1");
915 if (g_strcmp0(passphrase
, "") == 0)
917 passphrase2
= purple_request_fields_get_string(fields
, "passphrase2");
918 if (g_strcmp0(passphrase2
, "") == 0)
921 if (g_strcmp0(passphrase
, passphrase2
) != 0) {
922 purple_notify_error(notify_handle
,
923 _("Internal keyring settings"),
924 _("Passphrases do not match"), NULL
, NULL
);
928 if (purple_request_fields_get_bool(fields
, "encrypt_passwords") &&
929 !passphrase
&& !intkeyring_key
)
931 purple_notify_error(notify_handle
,
932 _("Internal keyring settings"),
933 _("You have to set up a Master password, if you want "
934 "to enable encryption"), NULL
, NULL
);
938 if (!purple_request_fields_get_bool(fields
, "encrypt_passwords") &&
941 purple_notify_error(notify_handle
,
942 _("Internal keyring settings"),
943 _("You don't need any master password, if you won't "
944 "enable passwords encryption"), NULL
, NULL
);
948 purple_prefs_set_string(INTKEYRING_PREFS
"encryption_method",
949 INTKEYRING_ENCRYPTION_METHOD
);
951 purple_prefs_set_int(INTKEYRING_PREFS
"pbkdf2_desired_iterations",
952 purple_request_fields_get_integer(fields
,
953 "pbkdf2_desired_iterations"));
955 if (passphrase
!= NULL
) {
956 if (!intkeyring_change_masterpw(passphrase
))
960 purple_prefs_set_bool(INTKEYRING_PREFS
"encrypt_passwords",
961 purple_request_fields_get_bool(fields
, "encrypt_passwords"));
963 purple_signal_emit(purple_keyring_get_handle(), "password-migration",
970 static PurplePluginInfo
*
971 plugin_query(GError
**error
)
973 const gchar
* const authors
[] = INTKEYRING_AUTHORS
;
975 return purple_plugin_info_new(
977 "name", INTKEYRING_NAME
,
978 "version", DISPLAY_VERSION
,
979 "category", N_("Keyring"),
980 "summary", "Internal Keyring Plugin",
981 "description", INTKEYRING_DESCRIPTION
,
983 "website", PURPLE_WEBSITE
,
984 "abi-version", PURPLE_ABI_VERSION
,
985 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
991 plugin_load(PurplePlugin
*plugin
, GError
**error
)
993 purple_prefs_add_none("/plugins/keyrings");
994 purple_prefs_add_none("/plugins/keyrings/internal");
995 purple_prefs_add_bool(INTKEYRING_PREFS
"encrypt_passwords", FALSE
);
996 purple_prefs_add_string(INTKEYRING_PREFS
"encryption_method",
997 INTKEYRING_ENCRYPTION_METHOD
);
998 purple_prefs_add_int(INTKEYRING_PREFS
"pbkdf2_desired_iterations",
999 INTKEYRING_PBKDF2_ITERATIONS
);
1000 purple_prefs_add_int(INTKEYRING_PREFS
"pbkdf2_iterations",
1001 INTKEYRING_PBKDF2_ITERATIONS
);
1002 purple_prefs_add_string(INTKEYRING_PREFS
"pbkdf2_salt", "");
1003 purple_prefs_add_string(INTKEYRING_PREFS
"key_verifier", "");
1005 keyring_handler
= purple_keyring_new();
1007 purple_keyring_set_name(keyring_handler
, _(INTKEYRING_NAME
));
1008 purple_keyring_set_id(keyring_handler
, INTKEYRING_ID
);
1009 purple_keyring_set_read_password(keyring_handler
,
1011 purple_keyring_set_save_password(keyring_handler
,
1013 purple_keyring_set_close_keyring(keyring_handler
,
1015 purple_keyring_set_import_password(keyring_handler
,
1016 intkeyring_import_password
);
1017 purple_keyring_set_export_password(keyring_handler
,
1018 intkeyring_export_password
);
1019 purple_keyring_set_read_settings(keyring_handler
,
1020 intkeyring_read_settings
);
1021 purple_keyring_set_apply_settings(keyring_handler
,
1022 intkeyring_apply_settings
);
1024 purple_keyring_register(keyring_handler
);
1030 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
1032 if (purple_keyring_get_inuse() == keyring_handler
) {
1033 g_set_error(error
, INTKEYRING_DOMAIN
, 0, "The keyring is currently "
1035 purple_debug_warning("keyring-internal",
1036 "keyring in use, cannot unload\n");
1042 purple_keyring_unregister(keyring_handler
);
1043 purple_keyring_free(keyring_handler
);
1044 keyring_handler
= NULL
;
1046 if (intkeyring_key
!= NULL
) {
1047 purple_debug_warning("keyring-internal", "Master key should be "
1048 "cleaned up at this point\n");
1049 intkeyring_buff_free(intkeyring_key
);
1050 intkeyring_key
= NULL
;
1056 PURPLE_PLUGIN_INIT(internal_keyring
, plugin_query
, plugin_load
, plugin_unload
);