[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