[Commits] [SCM] claws branch, master, updated. 3.13.2-14-gad9ddd7

ticho at claws-mail.org ticho at claws-mail.org
Thu Feb 4 22:21:52 CET 2016


The branch, master has been updated
       via  ad9ddd799eff250b2f7a7a8752a9ffed282c2f98 (commit)
       via  54adfb433b879fa0e9218ed329feb6d99caf0fb1 (commit)
      from  e7374d38e51fa38b5c2f06669be8c3f967c39ba1 (commit)

Summary of changes:
 configure.ac                               |   30 ++
 doc/src/password_encryption.txt            |   47 +++
 src/Makefile.am                            |    4 +
 src/imap.c                                 |   16 +-
 src/inc.c                                  |    3 +-
 src/news.c                                 |    3 +-
 src/password.c                             |  551 ++++++++++++++++++++++++++++
 src/password.h                             |   63 ++++
 src/password_gtk.c                         |  212 +++++++++++
 src/{gtk/pluginwindow.h => password_gtk.h} |   15 +-
 src/prefs_common.c                         |    6 +-
 src/prefs_common.h                         |    6 +
 src/prefs_gtk.c                            |   54 +--
 src/prefs_other.c                          |   94 ++++-
 src/send_message.c                         |    5 +-
 src/wizard.c                               |    9 +-
 16 files changed, 1057 insertions(+), 61 deletions(-)
 create mode 100644 doc/src/password_encryption.txt
 create mode 100644 src/password.c
 create mode 100644 src/password.h
 create mode 100644 src/password_gtk.c
 copy src/{gtk/pluginwindow.h => password_gtk.h} (78%)


- Log -----------------------------------------------------------------
commit ad9ddd799eff250b2f7a7a8752a9ffed282c2f98
Author: Andrej Kacian <ticho at claws-mail.org>
Date:   Thu Feb 4 22:02:35 2016 +0100

    Added password_encryption.txt to docs/src.

diff --git a/doc/src/password_encryption.txt b/doc/src/password_encryption.txt
new file mode 100644
index 0000000..aed0cb0
--- /dev/null
+++ b/doc/src/password_encryption.txt
@@ -0,0 +1,47 @@
+Unless --with-password-encryption=old is active, account passwords are
+stored encrypted using AES-256-CBC, using following scheme:
+----------------------------------------------------------------------
+
+Encryption/decryption key is either PASSCRYPT_KEY, or user-selected master password.
+
+We take the digest of the key using SHA-512, which gives us a 64 bytes
+long hash.
+
+The first half of the hash is XORed with the second (1st byte with
+33rd, 2nd with 34th, etc.). This is gives us 32 bytes, which is
+ey length required for AES-256-CBC.
+
+IV for the cipher is filled with random bytes.
+
+
+Encryption
+----------
+
+We prepare a buffer 128+blocksize bytes long, with one block of random
+data at the beginning, followed by the password we want to encrypt,
+rest is padded with zero bytes.
+
+We encrypt the buffer.
+
+We base64-encode the ciphertext, and store it as:
+"{algorithm}encodedciphertext"
+
+
+Decryption
+----------
+We strip the "{algorithm}" (after verifying that it matches what we
+expect) and base64-decode the remaining ciphertext.
+
+We decrypt the ciphertext.
+
+We discard the first block, and the rest is a zero-terminated string
+with our password.
+
+
+Why the random block at the beginning?
+--------------------------------------
+
+We are taking advantage of property of CBC mode where decryption with
+a wrong IV results in only first block being garbled. Therefore we
+prepend a random block to our plaintext before encryption, and discard
+first block from plaintext after decryption.

commit 54adfb433b879fa0e9218ed329feb6d99caf0fb1
Author: Andrej Kacian <ticho at claws-mail.org>
Date:   Sat Jan 16 22:13:53 2016 +0100

    Rewritten account passwords handling.
    
    Passwords are only decrypted before their actual use, not
    while loading from accountrc.
    Passwords are stored as "{algorithm}base64encodedciphertext",
    encrypted using AES-CBC cipher, with PASSCRYPT_KEY used as
    and encryption key.
    Optionally, the encryption key, also known as "master password"
    can be changed by user.

diff --git a/configure.ac b/configure.ac
index 8bc3db4..4e38388 100644
--- a/configure.ac
+++ b/configure.ac
@@ -519,6 +519,35 @@ if test x"$ac_cv_with_config_dir" = x""; then
 fi
 AC_DEFINE_UNQUOTED(CFG_RC_DIR, "$ac_cv_with_config_dir", Configuration directory)
 
+AC_ARG_WITH(password-encryption, [  --with-password-encryption=PROVIDER    Which cryptographic library to use for encrypting stored passwords (gnutls, old, default)],
+						pwd_crypto="$withval", pwd_crypto="default")
+
+if test x"$pwd_crypto" = xdefault; then
+dnl ===Default set to "old" for testing, remove the dnls to restore
+dnl ===intended functionality.
+dnl	if test x"$enable_gnutls" = xyes; then
+dnl		pwd_crypto="gnutls"
+dnl	else
+		pwd_crypto="old"
+dnl	fi
+fi
+
+case $pwd_crypto in
+	gnutls)
+		if test x"$enable_gnutls" = xno; then
+			AC_MSG_ERROR([GnuTLS password encryption requested but GnuTLS is not available.])
+		fi
+		AC_DEFINE(PASSWORD_CRYPTO_GNUTLS, 1, Use GnuTLS for stored password encryption)
+		;;
+	old)
+		AC_DEFINE(PASSWORD_CRYPTO_OLD, 1, Use old insecure method for stored password encryption)
+		;;
+	*)
+		AC_MSG_ERROR([Unknown password encryption provider requested.])
+		;;
+esac
+
+
 dnl ************************
 dnl ** GTK user interface **
 dnl ************************
@@ -1952,6 +1981,7 @@ echo "NetworkManager     : $enable_networkmanager"
 echo "Manual             : $enable_manual"
 echo "Generic UMPC code  : $enable_generic_umpc"
 echo "Config dir         : $ac_cv_with_config_dir"
+echo "Password crypto    : $pwd_crypto"
 
 echo "Plugins"
 echo "   Built:"
diff --git a/src/Makefile.am b/src/Makefile.am
index a3b2113..f958fd5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -176,6 +176,8 @@ claws_mail_SOURCES = \
 	news_gtk.c \
 	noticeview.c \
 	partial_download.c \
+	password.c \
+	password_gtk.c \
 	pop.c \
 	prefs_account.c \
 	prefs_actions.c \
@@ -291,6 +293,8 @@ claws_mailinclude_HEADERS = \
 	news_gtk.h \
 	noticeview.h \
 	partial_download.h \
+	password.h \
+	password_gtk.h \
 	pop.h \
 	prefs_account.h \
 	prefs_actions.h \
diff --git a/src/imap.c b/src/imap.c
index 60c8e31..8ccf871 100644
--- a/src/imap.c
+++ b/src/imap.c
@@ -71,6 +71,7 @@
 #include "account.h"
 #include "tags.h"
 #include "main.h"
+#include "password.h"
 
 typedef struct _IMAPFolder	IMAPFolder;
 typedef struct _IMAPSession	IMAPSession;
@@ -1263,7 +1264,7 @@ static gint imap_session_authenticate(IMAPSession *session,
 		Xstrdup_a(acc_pass, pass, {g_free(pass); return MAILIMAP_NO_ERROR;});
 		g_free(pass);
 	} else {
-		acc_pass = account->passwd;
+		acc_pass = password_decrypt(account->passwd, NULL);
 	}
 try_again:
 	pass = acc_pass;
@@ -1272,8 +1273,11 @@ try_again:
 		tmp_pass = input_dialog_query_password_keep(account->recv_server, 
 							    account->userid,
 							    &(account->session_passwd));
-		if (!tmp_pass)
+		if (!tmp_pass) {
+			memset(acc_pass, 0, strlen(acc_pass));
+			g_free(acc_pass);
 			return MAILIMAP_NO_ERROR;
+		}
 		Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;});
 		g_free(tmp_pass);
 	} else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) {
@@ -1282,6 +1286,8 @@ try_again:
 	if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) {
 		
 		if (!failed && !is_fatal(ok)) {
+			memset(acc_pass, 0, strlen(acc_pass));
+			g_free(acc_pass);
 			acc_pass = NULL;
 			failed = TRUE;
 			if (account->session_passwd != NULL) {
@@ -1295,11 +1301,15 @@ try_again:
 				mainwindow_show_error();
 			} else
 				alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
-		}		
+		}
 
+		g_free(acc_pass);
+		memset(acc_pass, 0, strlen(acc_pass));
 		return ok;
 	} 
 
+	memset(acc_pass, 0, strlen(acc_pass));
+	g_free(acc_pass);
 	statuswindow_pop_all();
 	session->authenticated = TRUE;
 	return MAILIMAP_NO_ERROR;
diff --git a/src/inc.c b/src/inc.c
index bfa5bf4..c4f82c2 100644
--- a/src/inc.c
+++ b/src/inc.c
@@ -59,6 +59,7 @@
 #include "log.h"
 #include "hooks.h"
 #include "logwindow.h"
+#include "password.h"
 
 extern SessionStats session_stats;
 
@@ -576,7 +577,7 @@ static gint inc_start(IncProgressDialog *inc_dialog)
 			/* NOP */;
 		} else if (pop3_session->ac_prefs->passwd)
 			pop3_session->pass =
-				g_strdup(pop3_session->ac_prefs->passwd);
+				password_decrypt(pop3_session->ac_prefs->passwd, NULL);
 		else {
 			gchar *pass;
 
diff --git a/src/news.c b/src/news.c
index fdcea33..ead70cb 100644
--- a/src/news.c
+++ b/src/news.c
@@ -48,6 +48,7 @@
 #include "statusbar.h"
 #include "codeconv.h"
 #include "utils.h"
+#include "password.h"
 #include "prefs_common.h"
 #include "prefs_account.h"
 #include "inputdialog.h"
@@ -405,7 +406,7 @@ static Session *news_session_new_for_folder(Folder *folder)
 		if (password_get(userid, ac->nntp_server, "nntp", port, &passwd)) {
 			/* NOP */;
 		} else if (ac->passwd && ac->passwd[0])
-			passwd = g_strdup(ac->passwd);
+			passwd = password_decrypt(ac->passwd, NULL);
 		else
 			passwd = input_dialog_query_password_keep(ac->nntp_server,
 								  userid,
diff --git a/src/password.c b/src/password.c
new file mode 100644
index 0000000..180f6f4
--- /dev/null
+++ b/src/password.c
@@ -0,0 +1,551 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2016 The Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#include "claws-features.h"
+#endif
+
+#ifdef PASSWORD_CRYPTO_GNUTLS
+# include <gnutls/gnutls.h>
+# include <gnutls/crypto.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifdef G_OS_UNIX
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
+#include "common/passcrypt.h"
+#include "common/utils.h"
+#include "account.h"
+#include "alertpanel.h"
+#include "inputdialog.h"
+#include "password.h"
+#include "prefs_common.h"
+
+#ifndef PASSWORD_CRYPTO_OLD
+static gchar *_master_password = NULL;
+
+static const gchar *master_password()
+{
+	gchar *input;
+	gboolean end = FALSE;
+
+	if (!prefs_common_get_prefs()->use_master_password) {
+		return PASSCRYPT_KEY;
+	}
+
+	if (_master_password != NULL) {
+		debug_print("Master password is in memory, offering it.\n");
+		return _master_password;
+	}
+
+	while (!end) {
+		input = input_dialog_with_invisible(_("Input master password"),
+				_("Input master password"), NULL);
+
+		if (input == NULL) {
+			debug_print("Cancel pressed at master password dialog.\n");
+			break;
+		}
+
+		if (master_password_is_correct(input)) {
+			debug_print("Entered master password seems to be correct, remembering it.\n");
+			_master_password = input;
+			end = TRUE;
+		} else {
+			alertpanel_error(_("Incorrect master password."));
+		}
+	}
+
+	return _master_password;
+}
+
+const gboolean master_password_is_set()
+{
+	if (prefs_common_get_prefs()->master_password_hash == NULL
+			|| strlen(prefs_common_get_prefs()->master_password_hash) == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+const gboolean master_password_is_correct(const gchar *input)
+{
+	gchar *hash;
+	gchar *stored_hash = prefs_common_get_prefs()->master_password_hash;
+	const GChecksumType hashtype = G_CHECKSUM_SHA512;
+	const gssize hashlen = g_checksum_type_get_length(hashtype);
+	gssize stored_len;
+
+	g_return_val_if_fail(input != NULL, FALSE);
+
+	if (stored_hash == NULL)
+		return FALSE;
+
+	stored_len = strlen(stored_hash);
+	g_return_val_if_fail(stored_len == 2*hashlen, FALSE);
+
+	hash = g_compute_checksum_for_string(hashtype, input, -1);
+
+	if (!strncasecmp(hash, stored_hash, stored_len)) {
+		g_free(hash);
+		return TRUE;
+	}
+	g_free(hash);
+
+	return FALSE;
+}
+
+void master_password_change(const gchar *newp)
+{
+	gchar *pwd, *newpwd;
+	const gchar *oldp;
+	GList *cur;
+	PrefsAccount *acc;
+
+	oldp = master_password();
+	g_return_if_fail(oldp != NULL);
+
+	/* Update master password hash in prefs */
+	if (prefs_common_get_prefs()->master_password_hash != NULL)
+		g_free(prefs_common_get_prefs()->master_password_hash);
+
+	if (newp != NULL) {
+		debug_print("Storing hash of new master password\n");
+		prefs_common_get_prefs()->master_password_hash =
+			g_compute_checksum_for_string(G_CHECKSUM_SHA512, newp, -1);
+	} else {
+		debug_print("Setting master_password_hash to NULL\n");
+		prefs_common_get_prefs()->master_password_hash = NULL;
+	}
+
+	/* Now go over all accounts, reencrypting their passwords using
+	 * the new master password. */
+
+	if (oldp == NULL)
+		oldp = PASSCRYPT_KEY;
+	if (newp == NULL)
+		newp = PASSCRYPT_KEY;
+
+	debug_print("Reencrypting all account passwords...\n");
+	for (cur = account_get_list(); cur != NULL; cur = cur->next) {
+		acc = (PrefsAccount *)cur->data;
+		debug_print("account %s\n", acc->account_name);
+
+		/* Password for receiving */
+		if (acc->passwd != NULL && strlen(acc->passwd) > 0) {
+			pwd = password_decrypt(acc->passwd, oldp);
+			if (pwd == NULL) {
+				debug_print("failed to decrypt recv password with old master password\n");
+			} else {
+				newpwd = password_encrypt(pwd, newp);
+				memset(pwd, 0, strlen(pwd));
+				g_free(pwd);
+				if (newpwd == NULL) {
+					debug_print("failed to encrypt recv password with new master password\n");
+				} else {
+					g_free(acc->passwd);
+					acc->passwd = newpwd;
+				}
+			}
+		}
+
+		/* Password for sending */
+		if (acc->smtp_passwd != NULL && strlen(acc->smtp_passwd) > 0) {
+			pwd = password_decrypt(acc->smtp_passwd, oldp);
+			if (pwd == NULL) {
+				debug_print("failed to decrypt smtp password with old master password\n");
+			} else {
+				newpwd = password_encrypt(pwd, newp);
+				memset(pwd, 0, strlen(pwd));
+				g_free(pwd);
+				if (newpwd == NULL) {
+					debug_print("failed to encrypt smtp password with new master password\n");
+				} else {
+					g_free(acc->smtp_passwd);
+					acc->smtp_passwd = newpwd;
+				}
+			}
+		}
+	}
+
+	/* If master password is currently in memory (entered by user),
+	 * get rid of it. User will have to enter the new one again. */
+	if (_master_password != NULL) {
+		memset(_master_password, 0, strlen(_master_password));
+		g_free(_master_password);
+	}
+	_master_password = NULL;
+}
+#endif
+
+gchar *password_encrypt_old(const gchar *password)
+{
+	if (!password || strlen(password) == 0) {
+		return NULL;
+	}
+
+	gchar *encrypted = g_strdup(password);
+	gchar *encoded, *result;
+	gsize len = strlen(password);
+
+	passcrypt_encrypt(encrypted, len);
+	encoded = g_base64_encode(encrypted, len);
+	g_free(encrypted);
+	result = g_strconcat("!", encoded, NULL);
+	g_free(encoded);
+
+	return result;
+}
+
+gchar *password_decrypt_old(const gchar *password)
+{
+	if (!password || strlen(password) == 0) {
+		return NULL;
+	}
+
+	if (*password != '!' || strlen(password) < 2) {
+		return NULL;
+	}
+
+	gsize len;
+	gchar *decrypted = g_base64_decode(password + 1, &len);
+
+	passcrypt_decrypt(decrypted, len);
+	return decrypted;
+}
+
+#ifdef PASSWORD_CRYPTO_GNUTLS
+#define BUFSIZE 128
+
+gchar *password_encrypt_gnutls(const gchar *password,
+		const gchar *encryption_password)
+{
+	/* Another, slightly inferior combination is AES-128-CBC + SHA-256.
+	 * Any block cipher in CBC mode with keysize N and a hash algo with
+	 * digest length 2*N would do. */
+	gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC;
+	gnutls_digest_algorithm_t digest = GNUTLS_DIG_SHA512;
+	gnutls_cipher_hd_t handle;
+	gnutls_datum_t key, iv;
+	int ivlen, keylen, digestlen, blocklen, ret, i;
+	unsigned char hashbuf[BUFSIZE], *buf, *encbuf, *base, *output;
+#ifdef G_OS_UNIX
+	int rnd;
+#endif
+
+	g_return_val_if_fail(password != NULL, NULL);
+	g_return_val_if_fail(encryption_password != NULL, NULL);
+
+	ivlen = gnutls_cipher_get_iv_size(algo);
+	keylen = gnutls_cipher_get_key_size(algo);
+	blocklen = gnutls_cipher_get_block_size(algo);
+	digestlen = gnutls_hash_get_len(digest);
+
+	/* Prepare key for cipher - first half of hash of passkey XORed with
+	 * the second. */
+	memset(&hashbuf, 0, BUFSIZE);
+	if ((ret = gnutls_hash_fast(digest, encryption_password,
+					strlen(encryption_password), &hashbuf)) < 0) {
+		debug_print("Hashing passkey failed: %s\n", gnutls_strerror(ret));
+		return NULL;
+	}
+	for (i = 0; i < digestlen/2; i++) {
+		hashbuf[i] = hashbuf[i] ^ hashbuf[i+digestlen/2];
+	}
+
+	key.data = malloc(keylen);
+	memcpy(key.data, &hashbuf, keylen);
+	key.size = keylen;
+
+#ifdef G_OS_UNIX
+	/* Prepare our source of random data. */
+	rnd = open("/dev/urandom", O_RDONLY);
+	if (rnd == -1) {
+		perror("fopen on /dev/urandom");
+		g_free(key.data);
+		g_free(iv.data);
+		return NULL;
+	}
+#endif
+
+	/* Prepare random IV for cipher */
+	iv.data = malloc(ivlen);
+	iv.size = ivlen;
+#ifdef G_OS_UNIX
+	ret = read(rnd, iv.data, ivlen);
+	if (ret != ivlen) {
+		perror("read into iv");
+		g_free(key.data);
+		g_free(iv.data);
+		close(rnd);
+		return NULL;
+	}
+#endif
+
+	/* Initialize the encryption */
+	ret = gnutls_cipher_init(&handle, algo, &key, &iv);
+	if (ret < 0) {
+		g_free(key.data);
+		g_free(iv.data);
+#ifdef G_OS_UNIX
+		close(rnd);
+#endif
+		return NULL;
+	}
+
+	/* Fill buf with one block of random data, our password, pad the
+	 * rest with zero bytes. */
+	buf = malloc(BUFSIZE + blocklen);
+	memset(buf, 0, BUFSIZE);
+#ifdef G_OS_UNIX
+	ret = read(rnd, buf, blocklen);
+	if (ret != blocklen) {
+		perror("read into buffer");
+		g_free(buf);
+		g_free(key.data);
+		g_free(iv.data);
+		close(rnd);
+		gnutls_cipher_deinit(handle);
+		return NULL;
+	}
+
+	/* We don't need any more random data. */
+	close(rnd);
+#endif
+
+	memcpy(buf + blocklen, password, strlen(password));
+
+	/* Encrypt into encbuf */
+	encbuf = malloc(BUFSIZE + blocklen);
+	memset(encbuf, 0, BUFSIZE + blocklen);
+	ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen,
+			encbuf, BUFSIZE + blocklen);
+	if (ret < 0) {
+		g_free(key.data);
+		g_free(iv.data);
+		g_free(buf);
+		g_free(encbuf);
+		gnutls_cipher_deinit(handle);
+		return NULL;
+	}
+
+	/* Cleanup */
+	gnutls_cipher_deinit(handle);
+	g_free(key.data);
+	g_free(iv.data);
+	g_free(buf);
+
+	/* And finally prepare the resulting string:
+	 * "{algorithm}base64encodedciphertext" */
+	base = g_base64_encode(encbuf, BUFSIZE);
+	g_free(encbuf);
+	output = g_strdup_printf("{%s}%s", gnutls_cipher_get_name(algo), base);
+	g_free(base);
+
+	return output;
+}
+
+gchar *password_decrypt_gnutls(const gchar *password,
+		const gchar *decryption_password)
+{
+	gchar **tokens, *tmp;
+	gnutls_cipher_algorithm_t algo;
+	gnutls_digest_algorithm_t digest = GNUTLS_DIG_UNKNOWN;
+	gnutls_cipher_hd_t handle;
+	gnutls_datum_t key, iv;
+	int ivlen, keylen, digestlen, blocklen, ret, i;
+	gsize len;
+	unsigned char hashbuf[BUFSIZE], *buf;
+#ifdef G_OS_UNIX
+	int rnd;
+#endif
+
+	g_return_val_if_fail(password != NULL, NULL);
+	g_return_val_if_fail(decryption_password != NULL, NULL);
+
+	tokens = g_strsplit_set(password, "{}", 3);
+
+	/* Parse the string, retrieving algorithm and encrypted data.
+	 * We expect "{algorithm}base64encodedciphertext". */
+	if (strlen(tokens[0]) != 0 ||
+			(algo = gnutls_cipher_get_id(tokens[1])) == GNUTLS_CIPHER_UNKNOWN ||
+			strlen(tokens[2]) == 0)
+		return NULL;
+
+	/* Our hash algo needs to have digest length twice as long as our
+	 * cipher algo's key length. */
+	if (algo == GNUTLS_CIPHER_AES_256_CBC) {
+		debug_print("Using AES-256-CBC + SHA-512 for decryption\n");
+		digest = GNUTLS_DIG_SHA512;
+	} else if (algo == GNUTLS_CIPHER_AES_128_CBC) {
+		debug_print("Using AES-128-CBC + SHA-256 for decryption\n");
+		digest = GNUTLS_DIG_SHA256;
+	}
+	if (digest == GNUTLS_DIG_UNKNOWN) {
+		debug_print("Password is encrypted with unsupported cipher, giving up.\n");
+		g_strfreev(tokens);
+		return NULL;
+	}
+
+	ivlen = gnutls_cipher_get_iv_size(algo);
+	keylen = gnutls_cipher_get_key_size(algo);
+	blocklen = gnutls_cipher_get_block_size(algo);
+	digestlen = gnutls_hash_get_len(digest);
+
+	/* Prepare key for cipher - first half of hash of passkey XORed with
+	 * the second. AES-256 has key length 32 and length of SHA-512 hash
+	 * is exactly twice that, 64. */
+	memset(&hashbuf, 0, BUFSIZE);
+	if ((ret = gnutls_hash_fast(digest, decryption_password,
+					strlen(decryption_password), &hashbuf)) < 0) {
+		debug_print("Hashing passkey failed: %s\n", gnutls_strerror(ret));
+		g_strfreev(tokens);
+		return NULL;
+	}
+	for (i = 0; i < digestlen/2; i++) {
+		hashbuf[i] = hashbuf[i] ^ hashbuf[i+digestlen/2];
+	}
+
+	key.data = malloc(keylen);
+	memcpy(key.data, &hashbuf, keylen);
+	key.size = keylen;
+
+#ifdef G_OS_UNIX
+	/* Prepare our source of random data. */
+	rnd = open("/dev/urandom", O_RDONLY);
+	if (rnd == -1) {
+		perror("fopen on /dev/urandom");
+		g_free(key.data);
+		g_free(iv.data);
+		g_strfreev(tokens);
+		return NULL;
+	}
+#endif
+
+	/* Prepare random IV for cipher */
+	iv.data = malloc(ivlen);
+	iv.size = ivlen;
+#ifdef G_OS_UNIX
+	ret = read(rnd, iv.data, ivlen);
+	if (ret != ivlen) {
+		perror("read into iv");
+		g_free(key.data);
+		g_free(iv.data);
+		g_strfreev(tokens);
+		close(rnd);
+		return NULL;
+	}
+
+	/* We don't need any more random data. */
+	close(rnd);
+#endif
+
+	/* Prepare encrypted password string for decryption. */
+	tmp = g_base64_decode(tokens[2], &len);
+	g_strfreev(tokens);
+
+	/* Initialize the decryption */
+	ret = gnutls_cipher_init(&handle, algo, &key, &iv);
+	if (ret < 0) {
+		debug_print("Cipher init failed: %s\n", gnutls_strerror(ret));
+		g_free(key.data);
+		g_free(iv.data);
+		return NULL;
+	}
+
+	buf = malloc(BUFSIZE + blocklen);
+	memset(buf, 0, BUFSIZE + blocklen);
+	ret = gnutls_cipher_decrypt2(handle, tmp, len,
+			buf, BUFSIZE + blocklen);
+	if (ret < 0) {
+		debug_print("Decryption failed: %s\n", gnutls_strerror(ret));
+		g_free(key.data);
+		g_free(iv.data);
+		g_free(buf);
+		gnutls_cipher_deinit(handle);
+		return NULL;
+	}
+
+	/* Cleanup */
+	gnutls_cipher_deinit(handle);
+	g_free(key.data);
+	g_free(iv.data);
+
+	tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE));
+	g_free(buf);
+	return tmp;
+}
+
+#undef BUFSIZE
+
+#endif
+
+gchar *password_encrypt(const gchar *password,
+		const gchar *encryption_password)
+{
+	if (password == NULL || strlen(password) == 0) {
+		return NULL;
+	}
+
+#ifndef PASSWORD_CRYPTO_OLD
+	if (encryption_password == NULL)
+		encryption_password = master_password();
+
+	return password_encrypt_real(password, encryption_password);
+#endif
+
+	return password_encrypt_old(password);
+}
+
+gchar *password_decrypt(const gchar *password,
+		const gchar *decryption_password)
+{
+	if (password == NULL || strlen(password) == 0) {
+		return NULL;
+	}
+
+	/* First, check if the password was possibly decrypted using old,
+	 * obsolete method */
+	if (*password == '!') {
+		debug_print("Trying to decrypt password using the old method...\n");
+		return password_decrypt_old(password);
+	}
+
+	/* Try available crypto backend */
+#ifndef PASSWORD_CRYPTO_OLD
+	if (decryption_password == NULL)
+		decryption_password = master_password();
+
+	if (*password == '{') {
+		debug_print("Trying to decrypt password...\n");
+		return password_decrypt_real(password, decryption_password);
+	}
+#endif
+
+	/* Fallback, in case the configuration is really old and
+	 * stored password in plaintext */
+	debug_print("Assuming password was stored plaintext, returning it unchanged\n");
+	return g_strdup(password);
+}
diff --git a/src/password.h b/src/password.h
new file mode 100644
index 0000000..df18e34
--- /dev/null
+++ b/src/password.h
@@ -0,0 +1,63 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2016 Claws Mail team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __PASSWORD_H
+#define __PASSWORD_H
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+
+#ifndef PASSWORD_CRYPTO_OLD
+const gboolean master_password_is_set();
+const gboolean master_password_is_correct(const gchar *input);
+void master_password_change(const gchar *newp);
+#endif
+
+/* Wrapper around the old, DES-CBC-broken implementation which
+ * returns a newly allocated string for the encrypt/decrypt result.
+ * This is for compatibility with with the rest of password-related
+ * functions.*/
+gchar *password_encrypt_old(const gchar *password);
+gchar *password_decrypt_old(const gchar *password);
+
+#ifdef PASSWORD_CRYPTO_GNUTLS
+/* GNUTLS implementation */
+gchar *password_encrypt_gnutls(const gchar *password,
+		const gchar *encryption_password);
+gchar *password_decrypt_gnutls(const gchar *password,
+		const gchar *decryption_password);
+#define password_encrypt_real(n, m) password_encrypt_gnutls(n, m)
+#define password_decrypt_real(n, m) password_decrypt_gnutls(n, m)
+#endif
+
+/* Wrapper function that will apply best encryption available,
+ * and return a string ready to be saved as-is in preferences. */
+gchar *password_encrypt(const gchar *password,
+		const gchar *encryption_password);
+
+/* This is a wrapper function that looks at the whole string from
+ * prefs (e.g. including the leading '!' for old implementation),
+ * and tries to do the smart thing. */
+gchar *password_decrypt(const gchar *password,
+		const gchar *decryption_password);
+
+#endif /* __PASSWORD_H */
diff --git a/src/password_gtk.c b/src/password_gtk.c
new file mode 100644
index 0000000..b2fb6b8
--- /dev/null
+++ b/src/password_gtk.c
@@ -0,0 +1,212 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2016 The Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#include "claws-features.h"
+#endif
+
+#ifndef PASSWORD_CRYPTO_OLD
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "common/utils.h"
+#include "gtk/manage_window.h"
+#include "gtk/gtkutils.h"
+#include "account.h"
+#include "alertpanel.h"
+#include "password.h"
+#include "prefs_common.h"
+
+static void entry_new1_activated(GtkEntry *entry, gpointer user_data)
+{
+	const gchar *text = gtk_entry_get_text(entry);
+
+	if (strlen(text) > 0)
+		gtk_widget_grab_focus(GTK_WIDGET(user_data));
+}
+
+struct _ctx {
+	gboolean done;
+	GtkWidget *dialog;
+	GtkWidget *entry_new1;
+	GtkWidget *entry_new2;
+};
+
+static void ok_button_clicked(GtkButton *button, gpointer user_data)
+{
+	struct _ctx *ctx = (struct _ctx *)user_data;
+	const gchar *new1 = gtk_entry_get_text(GTK_ENTRY(ctx->entry_new1));
+	const gchar *new2 = gtk_entry_get_text(GTK_ENTRY(ctx->entry_new2));
+
+	debug_print("OK button activated\n");
+
+	/* Now we check the new password - same in both entries. */
+	if (strcmp(new1, new2)) {
+		debug_print("passwords do not match\n");
+		alertpanel_warning(_("New passwords do not match, try again."));
+		gtk_entry_set_text(GTK_ENTRY(ctx->entry_new1), "");
+		gtk_entry_set_text(GTK_ENTRY(ctx->entry_new2), "");
+		gtk_widget_grab_focus(ctx->entry_new1);
+		return;
+	}
+
+	master_password_change(new1);
+
+	ctx->done = TRUE;
+	gtk_widget_destroy(ctx->dialog);
+	ctx->dialog = NULL;
+}
+
+static void cancel_button_clicked(GtkButton *button, gpointer user_data)
+{
+	struct _ctx *ctx = (struct _ctx *)user_data;
+	ctx->done = TRUE;
+	gtk_widget_destroy(ctx->dialog);
+	ctx->dialog = NULL;
+}
+
+static void dialog_destroy(GtkWidget *widget, gpointer user_data)
+{
+	struct _ctx *ctx = (struct _ctx *)user_data;
+	ctx->done = TRUE;
+	ctx->dialog = NULL;
+}
+
+void master_password_change_dialog()
+{
+	static PangoFontDescription *font_desc;
+	GtkWidget *dialog;
+	GtkWidget *vbox, *hbox;
+	GtkWidget *icon;
+	GtkWidget *msg_title, *msg_label;
+	GtkWidget *entry_new1, *entry_new2;
+	GtkWidget *confirm_area;
+	GtkWidget *ok_button, *cancel_button;
+	struct _ctx *ctx;
+
+	dialog = gtk_dialog_new();
+
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100);
+	gtk_window_set_title(GTK_WINDOW(dialog), "");
+
+	MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
+
+	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	gtk_box_set_spacing(GTK_BOX(vbox), 14);
+	hbox = gtk_hbox_new(FALSE, 12);
+	gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
+	gtk_widget_show(hbox);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_AUTHENTICATION,
+			GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment(GTK_MISC(icon), 0.5, 0.0);
+	gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0);
+
+	vbox = gtk_vbox_new(FALSE, 12);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
+	gtk_widget_show(vbox);
+
+	msg_title = gtk_label_new(_("Changing master password"));
+	gtk_misc_set_alignment(GTK_MISC(msg_title), 0, 0.5);
+	gtk_label_set_justify(GTK_LABEL(msg_title), GTK_JUSTIFY_LEFT);
+	gtk_label_set_use_markup (GTK_LABEL (msg_title), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), msg_title, FALSE, FALSE, 0);
+	gtk_label_set_line_wrap(GTK_LABEL(msg_title), TRUE);
+	if (!font_desc) {
+		gint size;
+
+		size = pango_font_description_get_size
+			(gtk_widget_get_style(msg_title)->font_desc);
+		font_desc = pango_font_description_new();
+		pango_font_description_set_weight
+			(font_desc, PANGO_WEIGHT_BOLD);
+		pango_font_description_set_size
+			(font_desc, size * PANGO_SCALE_LARGE);
+	}
+	if (font_desc)
+		gtk_widget_modify_font(msg_title, font_desc);
+
+	msg_label = gtk_label_new("<some useful text goes here>");
+	gtk_misc_set_alignment(GTK_MISC(msg_label), 0, 0.5);
+	gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT);
+	gtk_box_pack_start(GTK_BOX(vbox), msg_label, FALSE, FALSE, 0);
+	gtk_widget_show(msg_label);
+
+	entry_new1 = gtk_entry_new();
+	gtk_entry_set_visibility(GTK_ENTRY(entry_new1), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), entry_new1, FALSE, FALSE, 0);
+
+	entry_new2 = gtk_entry_new();
+	gtk_entry_set_visibility(GTK_ENTRY(entry_new2), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), entry_new2, FALSE, FALSE, 0);
+
+	gtkut_stock_button_set_create(&confirm_area,
+			&cancel_button, GTK_STOCK_CANCEL,
+			&ok_button, GTK_STOCK_OK,
+			NULL, NULL);
+
+	gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog))),
+			confirm_area, FALSE, FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
+
+	gtk_widget_grab_default(ok_button);
+
+	g_signal_connect(G_OBJECT(entry_new1), "activate",
+			G_CALLBACK(entry_new1_activated), entry_new2);
+	gtk_entry_set_activates_default(GTK_ENTRY(entry_new2), TRUE);
+
+	ctx = g_new(struct _ctx, 1);
+	ctx->done = FALSE;
+	ctx->dialog = dialog;
+	ctx->entry_new1 = entry_new1;
+	ctx->entry_new2 = entry_new2;
+
+	g_signal_connect(G_OBJECT(ok_button), "clicked",
+			G_CALLBACK(ok_button_clicked), ctx);
+	g_signal_connect(G_OBJECT(cancel_button), "clicked",
+			G_CALLBACK(cancel_button_clicked), ctx);
+
+	g_signal_connect(G_OBJECT(dialog), "destroy",
+			G_CALLBACK(dialog_destroy), ctx);
+
+	gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
+	gtk_window_present(GTK_WINDOW(dialog));
+
+	gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+	manage_window_set_transient(GTK_WINDOW(dialog));
+
+	while (!ctx->done)
+		gtk_main_iteration();
+
+	manage_window_focus_out(dialog, NULL, NULL);
+
+	if (ctx->dialog != NULL)
+		gtk_widget_destroy(ctx->dialog);
+
+	GTK_EVENTS_FLUSH();
+
+	g_free(ctx);
+}
+
+#endif /* !PASSWORD_CRYPTO_OLD */
diff --git a/src/password_gtk.h b/src/password_gtk.h
new file mode 100644
index 0000000..86fa9d2
--- /dev/null
+++ b/src/password_gtk.h
@@ -0,0 +1,31 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2016 Claws Mail team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __PASSWORD_GTK_H
+#define __PASSWORD_GTK_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef PASSWORD_CRYPTO_OLD
+void master_password_change_dialog();
+#endif
+
+#endif /* __PASSWORD_GTK_H */
diff --git a/src/prefs_common.c b/src/prefs_common.c
index 69f1e6a..50f9ebf 100644
--- a/src/prefs_common.c
+++ b/src/prefs_common.c
@@ -18,7 +18,7 @@
  */
 
 #ifdef HAVE_CONFIG_H
-#  include "config.h"
+#include "config.h"
 #include "claws-features.h"
 #endif
 
@@ -1189,6 +1189,10 @@ static PrefParam param[] = {
 	{"address_search_wildcard", "TRUE", &prefs_common.address_search_wildcard, P_BOOL,
 	 NULL, NULL, NULL},
 	{"enable_avatars", "3", &prefs_common.enable_avatars, P_INT, NULL, NULL, NULL},
+#ifndef PASSWORD_CRYPTO_OLD
+	{"use_master_password", FALSE, &prefs_common.use_master_password, P_BOOL, NULL, NULL, NULL },
+	{"master_password_hash", "", &prefs_common.master_password_hash, P_STRING, NULL, NULL, NULL },
+#endif
 
 	{NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
 };
diff --git a/src/prefs_common.h b/src/prefs_common.h
index 6c98ae1..ed86cd0 100644
--- a/src/prefs_common.h
+++ b/src/prefs_common.h
@@ -21,6 +21,7 @@
 #define __PREFS_COMMON_H__
 
 #ifdef HAVE_CONFIG_H
+#include "config.h"
 #include "claws-features.h"
 #endif
 
@@ -537,6 +538,11 @@ struct _PrefsCommon
 	gboolean address_search_wildcard;
 
 	guint enable_avatars;
+
+#ifndef PASSWORD_CRYPTO_OLD
+	gboolean use_master_password;
+	gchar *master_password_hash;
+#endif
 };
 
 extern PrefsCommon prefs_common;
diff --git a/src/prefs_gtk.c b/src/prefs_gtk.c
index 3525d19..68840bf 100644
--- a/src/prefs_gtk.c
+++ b/src/prefs_gtk.c
@@ -40,7 +40,7 @@
 #include "prefs_common.h"
 #include "utils.h"
 #include "gtkutils.h"
-#include "passcrypt.h"
+#include "password.h"
 #include "codeconv.h"
 
 #define CL(x)	(((gulong) (x) >> (gulong) 8) & 0xFFUL)
@@ -169,6 +169,7 @@ static void prefs_config_parse_one_line(PrefParam *param, const gchar *buf)
 
 		switch (param[i].type) {
 		case P_STRING:
+		case P_PASSWORD:
 		{
 			gchar *tmp = NULL;
 
@@ -216,23 +217,6 @@ static void prefs_config_parse_one_line(PrefParam *param, const gchar *buf)
 				/* be compatible and accept ints */
 				*((gulong *)param[i].data) = strtoul(value, 0, 10); 
 			break;
-		case P_PASSWORD:
-			g_free(*((gchar **)param[i].data));
-			if (value[0] == '!') {
-				gchar *tmp;
-				gsize len;
-
-				tmp = g_base64_decode(&value[1], &len);
-				passcrypt_decrypt(tmp, len);
-
-				*((gchar **)param[i].data) =
-					*tmp ? g_strdup(tmp) : NULL;
-				g_free(tmp);
-			} else {
-				*((gchar **)param[i].data) =
-					*value ? g_strdup(value) : NULL;
-			}
-			break;
 		default:
 			break;
 		}
@@ -340,6 +324,7 @@ gint prefs_write_param(PrefParam *param, FILE *fp)
 	for (i = 0; param[i].name != NULL; i++) {
 		switch (param[i].type) {
 		case P_STRING:
+		case P_PASSWORD:
 		{
 			gchar *tmp = NULL;
 
@@ -381,27 +366,6 @@ gint prefs_write_param(PrefParam *param, FILE *fp)
 			g_snprintf(buf, sizeof buf,  "%s=#%6.6lx\n", param[i].name,
 				   *((gulong *) param[i].data));
 			break;
-		case P_PASSWORD:
-			{
-				gchar *tmp = NULL, *tmp2 = NULL;
-
-				tmp = *((gchar **)param[i].data);
-				if (tmp) {
-					gint len;
-
-					tmp = g_strdup(tmp);
-					len = strlen(tmp);
-					passcrypt_encrypt(tmp, len);
-					tmp2 = g_base64_encode(tmp, len);
-					g_free(tmp);
-					tmp = tmp2;
-				}
-				g_snprintf(buf, sizeof(buf), "%s=!%s\n", param[i].name,
-					   tmp ?
-					   tmp : "");
-				g_free(tmp);
-			}
-			break;
 		default:
 			/* unrecognized, fail */
 			debug_print("Unrecognized parameter type\n");
@@ -662,7 +626,6 @@ void prefs_set_data_from_entry(PrefParam *pparam)
 
 	switch (pparam->type) {
 	case P_STRING:
-	case P_PASSWORD:
 		str = (gchar **)pparam->data;
 		g_free(*str);
 		*str = entry_str[0] ? g_strdup(entry_str) : NULL;
@@ -673,6 +636,11 @@ void prefs_set_data_from_entry(PrefParam *pparam)
 	case P_INT:
 		*((gint *)pparam->data) = atoi(entry_str);
 		break;
+	case P_PASSWORD:
+		str = (gchar **)pparam->data;
+		g_free(*str);
+		*str = password_encrypt(entry_str, NULL);
+		break;
 	default:
 		g_warning("Invalid PrefType for GtkEntry widget: %d",
 			  pparam->type);
@@ -705,7 +673,6 @@ void prefs_set_entry(PrefParam *pparam)
 
 	switch (pparam->type) {
 	case P_STRING:
-	case P_PASSWORD:
 		str = (gchar **)pparam->data;
 		gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
 				   *str ? *str : "");
@@ -718,6 +685,11 @@ void prefs_set_entry(PrefParam *pparam)
 		gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
 				   itos(*((gushort *)pparam->data)));
 		break;
+	case P_PASSWORD:
+		str = (gchar **)pparam->data;
+		gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
+				password_decrypt(*str, NULL));
+		break;
 	default:
 		g_warning("Invalid PrefType for GtkEntry widget: %d",
 			  pparam->type);
diff --git a/src/prefs_other.c b/src/prefs_other.c
index 83d5cf1..09a4b6c 100644
--- a/src/prefs_other.c
+++ b/src/prefs_other.c
@@ -17,7 +17,7 @@
  */
 
 #ifdef HAVE_CONFIG_H
-#  include "config.h"
+#include "config.h"
 #include "claws-features.h"
 #endif
 
@@ -37,6 +37,10 @@
 #include "gtk/gtkutils.h"
 #include "gtk/prefswindow.h"
 #include "combobox.h"
+#ifndef PASSWORD_CRYPTO_OLD
+#include "password.h"
+#include "password_gtk.h"
+#endif
 
 #include "manage_window.h"
 #ifdef HAVE_LIBETPAN
@@ -61,6 +65,9 @@ typedef struct _OtherPage
 	GtkWidget *checkbtn_real_time_sync;
 	GtkWidget *flush_metadata_faster_radiobtn;
 	GtkWidget *flush_metadata_safer_radiobtn;
+#ifndef PASSWORD_CRYPTO_OLD
+	GtkWidget *checkbtn_use_password;
+#endif
 } OtherPage;
 
 static struct KeybindDialog {
@@ -77,6 +84,10 @@ static gboolean prefs_keybind_key_pressed	(GtkWidget	*widget,
 						 gpointer	 data);
 static void prefs_keybind_cancel		(void);
 static void prefs_keybind_apply_clicked		(GtkWidget	*widget);
+#ifndef PASSWORD_CRYPTO_OLD
+static void prefs_change_master_password(GtkButton *button, gpointer data);
+static void prefs_use_password_toggled(GtkToggleButton *button, gpointer data);
+#endif
 
 
 static void prefs_keybind_select(void)
@@ -465,6 +476,13 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window,
 	GtkWidget *flush_metadata_faster_radiobtn;
 	GtkWidget *flush_metadata_safer_radiobtn;
 
+#ifndef PASSWORD_CRYPTO_OLD
+	GtkWidget *vbox_password;
+	GtkWidget *frame_password;
+	GtkWidget *checkbtn_use_password;
+	GtkWidget *button_change_password;
+#endif
+
 	gchar *shred_binary = NULL;
 
 	vbox1 = gtk_vbox_new (FALSE, VSPACING);
@@ -585,6 +603,31 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window,
 	PACK_CHECK_BUTTON (vbox2, checkbtn_real_time_sync,
 			   _("Synchronise offline folders as soon as possible"));
 
+#ifndef PASSWORD_CRYPTO_OLD
+	vbox_password = gtkut_get_options_frame(vbox1, &frame_password, _("Master password"));
+
+	PACK_CHECK_BUTTON(vbox_password, checkbtn_use_password,
+			_("Use a master password"));
+
+	CLAWS_SET_TIP(checkbtn_use_password,
+			_("If checked, your saved account passwords will be protected "
+				"by a master password. If no master password is set, "
+				"you will be prompted to set one."));
+
+	button_change_password = gtk_button_new_with_label(
+			_("Change master password"));
+	gtk_widget_show (button_change_password);
+	hbox1 = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox1);
+	gtk_box_pack_start (GTK_BOX (vbox_password), hbox1, FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox1), button_change_password,
+			FALSE, FALSE, 0);
+	g_signal_connect (G_OBJECT (checkbtn_use_password), "toggled",
+				G_CALLBACK (prefs_use_password_toggled), button_change_password);
+	g_signal_connect (G_OBJECT (button_change_password), "clicked",
+			  G_CALLBACK (prefs_change_master_password), NULL);
+#endif
+
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_addaddrbyclick), 
 		prefs_common.add_address_by_click);
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_confonexit), 
@@ -608,6 +651,13 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window,
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_real_time_sync), 
 		prefs_common.real_time_sync);
 
+#ifndef PASSWORD_CRYPTO_OLD
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_use_password),
+		prefs_common.use_master_password);
+	gtk_widget_set_sensitive(button_change_password,
+			prefs_common.use_master_password);
+#endif
+
 	prefs_other->checkbtn_addaddrbyclick = checkbtn_addaddrbyclick;
 	prefs_other->checkbtn_confonexit = checkbtn_confonexit;
 	prefs_other->checkbtn_cleanonexit = checkbtn_cleanonexit;
@@ -620,6 +670,9 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window,
 	prefs_other->checkbtn_real_time_sync = checkbtn_real_time_sync;
 	prefs_other->flush_metadata_safer_radiobtn = flush_metadata_safer_radiobtn;
 	prefs_other->flush_metadata_faster_radiobtn = flush_metadata_faster_radiobtn;
+#ifndef PASSWORD_CRYPTO_OLD
+	prefs_other->checkbtn_use_password = checkbtn_use_password;
+#endif
 	prefs_other->page.widget = vbox1;
 }
 
@@ -654,7 +707,27 @@ static void prefs_other_save(PrefsPage *_page)
 			GTK_TOGGLE_BUTTON(page->checkbtn_use_shred)); 
 	prefs_common.real_time_sync = 
 		gtk_toggle_button_get_active(
-			GTK_TOGGLE_BUTTON(page->checkbtn_real_time_sync)); 
+			GTK_TOGGLE_BUTTON(page->checkbtn_real_time_sync));
+
+#ifndef PASSWORD_CRYPTO_OLD
+	/* If we're disabling use of master password, we need to reencrypt
+	 * all account passwords with hardcoded key. */
+	if (!gtk_toggle_button_get_active(
+			GTK_TOGGLE_BUTTON(page->checkbtn_use_password))
+			&& master_password_is_set()) {
+		master_password_change(NULL);
+	}
+
+	if (gtk_toggle_button_get_active(
+			GTK_TOGGLE_BUTTON(page->checkbtn_use_password))
+			&& !master_password_is_set()) {
+		master_password_change_dialog();
+	}
+
+	prefs_common.use_master_password =
+		gtk_toggle_button_get_active(
+			GTK_TOGGLE_BUTTON(page->checkbtn_use_password));
+#endif
 
 	gtk_can_change_accels = gtk_toggle_button_get_active(
 		GTK_TOGGLE_BUTTON(page->checkbtn_gtk_can_change_accels));
@@ -709,3 +782,20 @@ void prefs_other_done(void)
 	prefs_gtk_unregister_page((PrefsPage *) prefs_other);
 	g_free(prefs_other);
 }
+
+#ifndef PASSWORD_CRYPTO_OLD
+void prefs_change_master_password(GtkButton *button, gpointer data)
+{
+	/* Call the password change dialog */
+	master_password_change_dialog();
+}
+
+void prefs_use_password_toggled(GtkToggleButton *chkbtn, gpointer data)
+{
+	GtkWidget *button = GTK_WIDGET(data);
+	gboolean active = gtk_toggle_button_get_active(chkbtn);
+
+	if (!active)
+		gtk_widget_set_sensitive(button, active);
+}
+#endif
diff --git a/src/send_message.c b/src/send_message.c
index 18f7f3a..07e3583 100644
--- a/src/send_message.c
+++ b/src/send_message.c
@@ -54,6 +54,7 @@
 #include "gtkutils.h"
 #include "inc.h"
 #include "log.h"
+#include "password.h"
 
 typedef struct _SendProgressDialog	SendProgressDialog;
 
@@ -305,7 +306,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g
 					/* NOP */;
 				} else if (ac_prefs->smtp_passwd)
 					smtp_session->pass =
-						g_strdup(ac_prefs->smtp_passwd);
+						password_decrypt(ac_prefs->smtp_passwd, NULL);
 				else {
 					smtp_session->pass =
 						input_dialog_query_password_keep
@@ -324,7 +325,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g
 							&(smtp_session->pass))) {
 					/* NOP */;
 				} else if (ac_prefs->passwd)
-					smtp_session->pass = g_strdup(ac_prefs->passwd);
+					smtp_session->pass = password_decrypt(ac_prefs->passwd, NULL);
 				else {
 					smtp_session->pass =
 						input_dialog_query_password_keep
diff --git a/src/wizard.c b/src/wizard.c
index e2e9a91..a13a4f2 100644
--- a/src/wizard.c
+++ b/src/wizard.c
@@ -50,6 +50,7 @@
 #endif
 #include "prefs_common.h"
 #include "combobox.h"
+#include "password.h"
 
 typedef enum
 {
@@ -756,13 +757,13 @@ static gboolean wizard_write_config(WizardWindow *wizard)
 
 	prefs_account->userid = g_strdup(
 				gtk_entry_get_text(GTK_ENTRY(wizard->recv_username)));
-	prefs_account->passwd = g_strdup(
-				gtk_entry_get_text(GTK_ENTRY(wizard->recv_password)));
+	prefs_account->passwd = password_encrypt(
+				gtk_entry_get_text(GTK_ENTRY(wizard->recv_password)), NULL);
 
 	prefs_account->smtp_userid = g_strdup(
 				gtk_entry_get_text(GTK_ENTRY(wizard->smtp_username)));
-	prefs_account->smtp_passwd = g_strdup(
-				gtk_entry_get_text(GTK_ENTRY(wizard->smtp_password)));
+	prefs_account->smtp_passwd = password_encrypt(
+				gtk_entry_get_text(GTK_ENTRY(wizard->smtp_password)), NULL);
 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wizard->smtp_auth))) {
 		prefs_account->use_smtp_auth = TRUE;
 	}

-----------------------------------------------------------------------


hooks/post-receive
-- 
Claws Mail


More information about the Commits mailing list