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 < NUM_PWS_TYPES, NULL);
49 g_return_val_if_fail(block_name != NULL, NULL);
51 for (item = _password_store; item != NULL; item = item->next) {
52 block = (PasswordBlock *)item->data;
53 if (block->block_type == block_type &&
54 !strcmp(block->block_name, block_name))
61 static gboolean _hash_equal_func(gconstpointer a, gconstpointer b)
63 if (g_strcmp0((const gchar *)a, (const gchar *)b) == 0)
68 /* Creates a new, empty block and adds it to the pwdstore. */
69 static PasswordBlock *_new_block(PasswordBlockType block_type,
70 const gchar *block_name)
74 g_return_val_if_fail(block_type < NUM_PWS_TYPES, NULL);
75 g_return_val_if_fail(block_name != NULL, NULL);
77 /* First check to see if the block doesn't already exist. */
78 if (_get_block(block_type, block_name)) {
79 debug_print("Block (%d/%s) already exists.\n",
80 block_type, block_name);
84 /* Let's create an empty block, and add it to pwdstore. */
85 block = g_new0(PasswordBlock, 1);
86 block->block_type = block_type;
87 block->block_name = g_strdup(block_name);
88 block->entries = g_hash_table_new_full(g_str_hash,
89 (GEqualFunc)_hash_equal_func,
91 debug_print("Created password block (%d/%s)\n",
92 block_type, block_name);
94 _password_store = g_slist_append(_password_store, block);
99 /*************************************************************/
101 /* Stores a password. */
102 gboolean passwd_store_set(PasswordBlockType block_type,
103 const gchar *block_name,
104 const gchar *password_id,
105 const gchar *password,
109 PasswordBlock *block;
110 gchar *encrypted_password;
112 g_return_val_if_fail(block_type < NUM_PWS_TYPES, FALSE);
113 g_return_val_if_fail(block_name != NULL, FALSE);
114 g_return_val_if_fail(password_id != NULL, FALSE);
116 /* Empty password string equals null password for us. */
117 if (password == NULL || strlen(password) == 0)
122 debug_print("%s password '%s' in block (%d/%s)%s\n",
123 (p == NULL ? "Deleting" : "Storing"),
124 password_id, block_type, block_name,
125 (encrypted ? ", already encrypted" : "") );
127 /* find correct block (create if needed) */
128 if ((block = _get_block(block_type, block_name)) == NULL) {
129 /* If caller wants to delete a password, and even its block
130 * doesn't exist, we're done. */
134 if ((block = _new_block(block_type, block_name)) == NULL) {
135 debug_print("Could not create password block (%d/%s)\n",
136 block_type, block_name);
142 /* NULL password was passed to us, so delete the entry with
143 * corresponding id */
144 g_hash_table_remove(block->entries, password_id);
147 /* encrypt password before saving it */
148 if ((encrypted_password =
149 password_encrypt(p, NULL)) == NULL) {
150 debug_print("Could not encrypt password '%s' for block (%d/%s).\n",
151 password_id, block_type, block_name);
155 /* password is already in encrypted form already */
156 encrypted_password = g_strdup(p);
159 /* add encrypted password to the block */
160 g_hash_table_insert(block->entries,
161 g_strdup(password_id),
168 /* Retrieves a password. */
169 gchar *passwd_store_get(PasswordBlockType block_type,
170 const gchar *block_name,
171 const gchar *password_id)
173 PasswordBlock *block;
174 gchar *encrypted_password, *password;
176 g_return_val_if_fail(block_type < NUM_PWS_TYPES, NULL);
177 g_return_val_if_fail(block_name != NULL, NULL);
178 g_return_val_if_fail(password_id != NULL, NULL);
180 debug_print("Getting password '%s' from block (%d/%s)\n",
181 password_id, block_type, block_name);
183 /* find correct block */
184 if ((block = _get_block(block_type, block_name)) == NULL) {
185 debug_print("Block (%d/%s) not found.\n", block_type, block_name);
189 /* grab pointer to encrypted password */
190 if ((encrypted_password =
191 g_hash_table_lookup(block->entries, password_id)) == NULL) {
192 debug_print("Password '%s' in block (%d/%s) not found.\n",
193 password_id, block_type, block_name);
197 /* decrypt password */
199 password_decrypt(encrypted_password, NULL)) == NULL) {
200 debug_print("Could not decrypt password '%s' for block (%d/%s).\n",
201 password_id, block_type, block_name);
205 /* return decrypted password */
209 gboolean passwd_store_delete_block(PasswordBlockType block_type,
210 const gchar *block_name)
212 PasswordBlock *block;
214 g_return_val_if_fail(block_type < NUM_PWS_TYPES, FALSE);
215 g_return_val_if_fail(block_name != NULL, FALSE);
217 debug_print("Deleting block (%d/%s)\n", block_type, block_name);
219 /* find correct block */
220 if ((block = _get_block(block_type, block_name)) == NULL) {
221 debug_print("Block (%d/%s) not found.\n", block_type, block_name);
225 g_hash_table_destroy(block->entries);
226 block->entries = NULL;
230 gboolean passwd_store_set_account(gint account_id,
231 const gchar *password_id,
232 const gchar *password,
235 gchar *uid = g_strdup_printf("%d", account_id);
236 gboolean ret = passwd_store_set(PWS_ACCOUNT, uid,
237 password_id, password, encrypted);
242 gchar *passwd_store_get_account(gint account_id,
243 const gchar *password_id)
245 gchar *uid = g_strdup_printf("%d", account_id);
246 gchar *ret = passwd_store_get(PWS_ACCOUNT, uid, password_id);
251 /* Reencrypts all stored passwords. */
252 void passwd_store_reencrypt_all(const gchar *old_mpwd,
253 const gchar *new_mpwd)
255 PasswordBlock *block;
258 gchar *encrypted_password, *decrypted_password, *key;
260 g_return_if_fail(old_mpwd != NULL);
261 g_return_if_fail(new_mpwd != NULL);
263 for (item = _password_store; item != NULL; item = item->next) {
264 block = (PasswordBlock *)item->data;
266 continue; /* Just in case. */
268 debug_print("Reencrypting passwords in block (%d/%s).\n",
269 block->block_type, block->block_name);
271 if (g_hash_table_size(block->entries) == 0)
274 keys = g_hash_table_get_keys(block->entries);
275 for (eitem = keys; eitem != NULL; eitem = eitem->next) {
276 key = (gchar *)eitem->data;
277 if ((encrypted_password =
278 g_hash_table_lookup(block->entries, key)) == NULL)
281 if ((decrypted_password =
282 password_decrypt(encrypted_password, old_mpwd)) == NULL) {
283 debug_print("Reencrypt: couldn't decrypt password for '%s'.\n", key);
287 encrypted_password = password_encrypt(decrypted_password, new_mpwd);
288 memset(decrypted_password, 0, strlen(decrypted_password));
289 g_free(decrypted_password);
290 if (encrypted_password == NULL) {
291 debug_print("Reencrypt: couldn't encrypt password for '%s'.\n", key);
295 g_hash_table_insert(block->entries, g_strdup(key), encrypted_password);
301 debug_print("Reencrypting done.\n");
304 static gint _write_to_file(FILE *fp)
306 PasswordBlock *block;
309 gchar *typestr, *line, *key, *pwd;
311 for (item = _password_store; item != NULL; item = item->next) {
312 block = (PasswordBlock*)item->data;
314 continue; /* Just in case. */
316 /* Do not save empty blocks. */
317 if (g_hash_table_size(block->entries) == 0)
320 /* Prepare the section header string and write it out. */
322 if (block->block_type == PWS_CORE) {
324 } else if (block->block_type == PWS_ACCOUNT) {
326 } else if (block->block_type == PWS_PLUGIN) {
329 line = g_strdup_printf("[%s:%s]\n", typestr, block->block_name);
331 if (fputs(line, fp) == EOF) {
332 FILE_OP_ERROR("password store", "fputs");
338 /* Now go through all passwords in the block and write each out. */
339 keys = g_hash_table_get_keys(block->entries);
340 for (eitem = keys; eitem != NULL; eitem = eitem->next) {
341 key = (gchar *)eitem->data;
342 if ((pwd = g_hash_table_lookup(block->entries, key)) == NULL)
345 line = g_strdup_printf("%s %s\n", key, pwd);
346 if (fputs(line, fp) == EOF) {
347 FILE_OP_ERROR("password store", "fputs");
355 if (item->next != NULL && fputs("\n", fp) == EOF) {
356 FILE_OP_ERROR("password store", "fputs");
365 void passwd_store_write_config(void)
370 debug_print("Writing password store...\n");
372 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
373 PASSWORD_STORE_RC, NULL);
375 if ((pfile = prefs_write_open(rcpath)) == NULL) {
376 g_warning("failed to open password store file for writing");
383 if (_write_to_file(pfile->fp) < 0) {
384 g_warning("failed to write password store to file");
385 prefs_file_close_revert(pfile);
386 } else if (prefs_file_close(pfile) < 0) {
387 g_warning("failed to properly close password store file after writing");
391 void passwd_store_read_config(void)
393 gchar *rcpath, *contents, **lines, **line, *typestr, *name;
394 GError *error = NULL;
396 PasswordBlock *block = NULL;
397 PasswordBlockType type;
399 /* TODO: passwd_store_clear(); */
401 debug_print("Reading password store from file...\n");
403 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
404 PASSWORD_STORE_RC, NULL);
406 if (!g_file_get_contents(rcpath, &contents, NULL, &error)) {
407 g_warning("couldn't read password store from file: %s", error->message);
414 lines = g_strsplit(contents, "\n", -1);
418 while (lines[i] != NULL) {
419 if (*lines[i] == '[') {
420 /* Beginning of a new block */
421 line = g_strsplit_set(lines[i], "[:]", -1);
422 if (line[0] != NULL && strlen(line[0]) == 0
423 && line[1] != NULL && strlen(line[1]) > 0
424 && line[2] != NULL && strlen(line[2]) > 0
425 && line[3] != NULL && strlen(line[3]) == 0) {
428 if (!strcmp(typestr, "core")) {
430 } else if (!strcmp(typestr, "account")) {
432 } else if (!strcmp(typestr, "plugin")) {
435 debug_print("Unknown password block type: '%s'\n", typestr);
440 if ((block = _new_block(type, name)) == NULL) {
441 debug_print("Duplicate password block, ignoring: (%d/%s)\n",
448 } else if (strlen(lines[i]) > 0 && block != NULL) {
449 /* If we have started a password block, test for a
450 * "password_id = password" line. */
451 line = g_strsplit(lines[i], " ", -1);
452 if (line[0] != NULL && strlen(line[0]) > 0
453 && line[1] != NULL && strlen(line[1]) > 0
454 && line[2] == NULL) {
455 debug_print("Adding password '%s'\n", line[0]);
456 g_hash_table_insert(block->entries,
457 g_strdup(line[0]), g_strdup(line[1]));