2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2016 The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
25 #ifdef PASSWORD_CRYPTO_GNUTLS
26 # include <gnutls/gnutls.h>
27 # include <gnutls/crypto.h>
31 #include <glib/gi18n.h>
33 #include "common/defs.h"
34 #include "common/utils.h"
35 #include "passwordstore.h"
37 #include "prefs_gtk.h"
39 static GSList
*_password_store
;
41 /* Finds password block of given type and name in the pwdstore. */
42 static PasswordBlock
*_get_block(PasswordBlockType block_type
,
43 const gchar
*block_name
)
48 g_return_val_if_fail(block_type
>= 0 && block_type
< NUM_PWS_TYPES
,
50 g_return_val_if_fail(block_name
!= NULL
, NULL
);
52 for (item
= _password_store
; item
!= NULL
; item
= item
->next
) {
53 block
= (PasswordBlock
*)item
->data
;
54 if (block
->block_type
== block_type
&&
55 !strcmp(block
->block_name
, block_name
))
62 static gboolean
_hash_equal_func(gconstpointer a
, gconstpointer b
)
64 if (g_strcmp0((const gchar
*)a
, (const gchar
*)b
) == 0)
69 /* Creates a new, empty block and adds it to the pwdstore. */
70 static PasswordBlock
*_new_block(PasswordBlockType block_type
,
71 const gchar
*block_name
)
75 g_return_val_if_fail(block_type
>= 0 && block_type
< NUM_PWS_TYPES
,
77 g_return_val_if_fail(block_name
!= NULL
, NULL
);
79 /* First check to see if the block doesn't already exist. */
80 if (_get_block(block_type
, block_name
)) {
81 debug_print("Block (%d/%s) already exists.\n",
82 block_type
, block_name
);
86 /* Let's create an empty block, and add it to pwdstore. */
87 block
= g_new0(PasswordBlock
, 1);
88 block
->block_type
= block_type
;
89 block
->block_name
= g_strdup(block_name
);
90 block
->entries
= g_hash_table_new_full(g_str_hash
,
91 (GEqualFunc
)_hash_equal_func
,
93 debug_print("Created password block (%d/%s)\n",
94 block_type
, block_name
);
96 _password_store
= g_slist_append(_password_store
, block
);
101 ///////////////////////////////////////////////////////////////
103 /* Stores a password. */
104 gboolean
passwd_store_set(PasswordBlockType block_type
,
105 const gchar
*block_name
,
106 const gchar
*password_id
,
107 const gchar
*password
,
111 PasswordBlock
*block
;
112 gchar
*encrypted_password
;
114 g_return_val_if_fail(block_type
>= 0 && block_type
< NUM_PWS_TYPES
,
116 g_return_val_if_fail(block_name
!= NULL
, FALSE
);
117 g_return_val_if_fail(password_id
!= NULL
, FALSE
);
119 /* Empty password string equals null password for us. */
120 if (password
== NULL
|| strlen(password
) == 0)
125 debug_print("%s password '%s' in block (%d/%s)%s\n",
126 (p
== NULL
? "Deleting" : "Storing"),
127 password_id
, block_type
, block_name
,
128 (encrypted
? ", already encrypted" : "") );
130 // find correct block (create if needed)
131 if ((block
= _get_block(block_type
, block_name
)) == NULL
) {
132 /* If caller wants to delete a password, and even its block
133 * doesn't exist, we're done. */
137 if ((block
= _new_block(block_type
, block_name
)) == NULL
) {
138 debug_print("Could not create password block (%d/%s)\n",
139 block_type
, block_name
);
145 /* NULL password was passed to us, so delete the entry with
146 * corresponding id */
147 g_hash_table_remove(block
->entries
, password_id
);
150 /* encrypt password before saving it */
151 if ((encrypted_password
=
152 password_encrypt(p
, NULL
)) == NULL
) {
153 debug_print("Could not encrypt password '%s' for block (%d/%s).\n",
154 password_id
, block_type
, block_name
);
158 /* password is already in encrypted form already */
159 encrypted_password
= g_strdup(p
);
162 // add encrypted password to the block
163 g_hash_table_insert(block
->entries
,
164 g_strdup(password_id
),
171 /* Retrieves a password. */
172 gchar
*passwd_store_get(PasswordBlockType block_type
,
173 const gchar
*block_name
,
174 const gchar
*password_id
)
176 PasswordBlock
*block
;
177 gchar
*encrypted_password
, *password
;
179 g_return_val_if_fail(block_type
>= 0 && block_type
< NUM_PWS_TYPES
,
181 g_return_val_if_fail(block_name
!= NULL
, NULL
);
182 g_return_val_if_fail(password_id
!= NULL
, NULL
);
184 debug_print("Getting password '%s' from block (%d/%s)\n",
185 password_id
, block_type
, block_name
);
187 // find correct block
188 if ((block
= _get_block(block_type
, block_name
)) == NULL
) {
189 debug_print("Block (%d/%s) not found.\n", block_type
, block_name
);
193 // grab pointer to encrypted password
194 if ((encrypted_password
=
195 g_hash_table_lookup(block
->entries
, password_id
)) == NULL
) {
196 debug_print("Password '%s' in block (%d/%s) not found.\n",
197 password_id
, block_type
, block_name
);
203 password_decrypt(encrypted_password
, NULL
)) == NULL
) {
204 debug_print("Could not decrypt password '%s' for block (%d/%s).\n",
205 password_id
, block_type
, block_name
);
209 // return decrypted password
213 gboolean
passwd_store_delete_block(PasswordBlockType block_type
,
214 const gchar
*block_name
)
216 PasswordBlock
*block
;
218 g_return_val_if_fail(block_type
>= 0 && block_type
< NUM_PWS_TYPES
,
220 g_return_val_if_fail(block_name
!= NULL
, FALSE
);
222 debug_print("Deleting block (%d/%s)\n", block_type
, block_name
);
224 // find correct block
225 if ((block
= _get_block(block_type
, block_name
)) == NULL
) {
226 debug_print("Block (%d/%s) not found.\n", block_type
, block_name
);
230 g_hash_table_destroy(block
->entries
);
231 block
->entries
= NULL
;
235 gboolean
passwd_store_set_account(gint account_id
,
236 const gchar
*password_id
,
237 const gchar
*password
,
240 gchar
*uid
= g_strdup_printf("%d", account_id
);
241 gboolean ret
= passwd_store_set(PWS_ACCOUNT
, uid
,
242 password_id
, password
, encrypted
);
247 gchar
*passwd_store_get_account(gint account_id
,
248 const gchar
*password_id
)
250 gchar
*uid
= g_strdup_printf("%d", account_id
);
251 gchar
*ret
= passwd_store_get(PWS_ACCOUNT
, uid
, password_id
);
256 /* Reencrypts all stored passwords. */
257 void passwd_store_reencrypt_all(const gchar
*old_mpwd
,
258 const gchar
*new_mpwd
)
260 PasswordBlock
*block
;
263 gchar
*encrypted_password
, *decrypted_password
, *key
;
265 g_return_if_fail(old_mpwd
!= NULL
);
266 g_return_if_fail(new_mpwd
!= NULL
);
268 for (item
= _password_store
; item
!= NULL
; item
= item
->next
) {
269 block
= (PasswordBlock
*)item
->data
;
271 continue; /* Just in case. */
273 debug_print("Reencrypting passwords in block (%d/%s).\n",
274 block
->block_type
, block
->block_name
);
276 if (g_hash_table_size(block
->entries
) == 0)
279 keys
= g_hash_table_get_keys(block
->entries
);
280 for (eitem
= keys
; eitem
!= NULL
; eitem
= eitem
->next
) {
281 key
= (gchar
*)eitem
->data
;
282 if ((encrypted_password
=
283 g_hash_table_lookup(block
->entries
, key
)) == NULL
)
286 if ((decrypted_password
=
287 password_decrypt(encrypted_password
, old_mpwd
)) == NULL
) {
288 debug_print("Reencrypt: couldn't decrypt password for '%s'.\n", key
);
292 encrypted_password
= password_encrypt(decrypted_password
, new_mpwd
);
293 memset(decrypted_password
, 0, strlen(decrypted_password
));
294 g_free(decrypted_password
);
295 if (encrypted_password
== NULL
) {
296 debug_print("Reencrypt: couldn't encrypt password for '%s'.\n", key
);
300 g_hash_table_insert(block
->entries
, g_strdup(key
), encrypted_password
);
306 debug_print("Reencrypting done.\n");
309 static gint
_write_to_file(FILE *fp
)
311 PasswordBlock
*block
;
314 gchar
*typestr
, *line
, *key
, *pwd
;
316 for (item
= _password_store
; item
!= NULL
; item
= item
->next
) {
317 block
= (PasswordBlock
*)item
->data
;
319 continue; /* Just in case. */
321 /* Do not save empty blocks. */
322 if (g_hash_table_size(block
->entries
) == 0)
325 /* Prepare the section header string and write it out. */
327 if (block
->block_type
== PWS_CORE
) {
329 } else if (block
->block_type
== PWS_ACCOUNT
) {
331 } else if (block
->block_type
== PWS_PLUGIN
) {
334 line
= g_strdup_printf("[%s:%s]\n", typestr
, block
->block_name
);
336 if (fputs(line
, fp
) == EOF
) {
337 FILE_OP_ERROR("password store", "fputs");
343 /* Now go through all passwords in the block and write each out. */
344 keys
= g_hash_table_get_keys(block
->entries
);
345 for (eitem
= keys
; eitem
!= NULL
; eitem
= eitem
->next
) {
346 key
= (gchar
*)eitem
->data
;
347 if ((pwd
= g_hash_table_lookup(block
->entries
, key
)) == NULL
)
350 line
= g_strdup_printf("%s %s\n", key
, pwd
);
351 if (fputs(line
, fp
) == EOF
) {
352 FILE_OP_ERROR("password store", "fputs");
360 if (item
->next
!= NULL
&& fputs("\n", fp
) == EOF
) {
361 FILE_OP_ERROR("password store", "fputs");
370 void passwd_store_write_config(void)
375 debug_print("Writing password store...\n");
377 rcpath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
378 PASSWORD_STORE_RC
, NULL
);
380 if ((pfile
= prefs_write_open(rcpath
)) == NULL
) {
381 g_warning("failed to open password store file for writing");
388 if (_write_to_file(pfile
->fp
) < 0) {
389 g_warning("failed to write password store to file");
390 prefs_file_close_revert(pfile
);
391 } else if (prefs_file_close(pfile
) < 0) {
392 g_warning("failed to properly close password store file after writing");
396 void passwd_store_read_config(void)
398 gchar
*rcpath
, *contents
, **lines
, **line
, *typestr
, *name
;
399 GError
*error
= NULL
;
401 PasswordBlock
*block
= NULL
;
402 PasswordBlockType type
;
404 // TODO: passwd_store_clear();
406 debug_print("Reading password store from file...\n");
408 rcpath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
409 PASSWORD_STORE_RC
, NULL
);
411 if (!g_file_get_contents(rcpath
, &contents
, NULL
, &error
)) {
412 g_warning("couldn't read password store from file: %s", error
->message
);
419 lines
= g_strsplit(contents
, "\n", -1);
423 while (lines
[i
] != NULL
) {
424 if (*lines
[i
] == '[') {
425 /* Beginning of a new block */
426 line
= g_strsplit_set(lines
[i
], "[:]", -1);
427 if (line
[0] != NULL
&& strlen(line
[0]) == 0
428 && line
[1] != NULL
&& strlen(line
[1]) > 0
429 && line
[2] != NULL
&& strlen(line
[2]) > 0
430 && line
[3] != NULL
&& strlen(line
[3]) == 0) {
433 if (!strcmp(typestr
, "core")) {
435 } else if (!strcmp(typestr
, "account")) {
437 } else if (!strcmp(typestr
, "plugin")) {
440 debug_print("Unknown password block type: '%s'\n", typestr
);
445 if ((block
= _new_block(type
, name
)) == NULL
) {
446 debug_print("Duplicate password block, ignoring: (%d/%s)\n",
453 } else if (strlen(lines
[i
]) > 0 && block
!= NULL
) {
454 /* If we have started a password block, test for a
455 * "password_id = password" line. */
456 line
= g_strsplit(lines
[i
], " ", -1);
457 if (line
[0] != NULL
&& strlen(line
[0]) > 0
458 && line
[1] != NULL
&& strlen(line
[1]) > 0
459 && line
[2] == NULL
) {
460 debug_print("Adding password '%s'\n", line
[0]);
461 g_hash_table_insert(block
->entries
,
462 g_strdup(line
[0]), g_strdup(line
[1]));