Rewritten account passwords handling.
authorAndrej Kacian <ticho@claws-mail.org>
Sat, 16 Jan 2016 21:13:53 +0000 (22:13 +0100)
committerAndrej Kacian <ticho@claws-mail.org>
Thu, 4 Feb 2016 21:04:48 +0000 (22:04 +0100)
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.

15 files changed:
configure.ac
src/Makefile.am
src/imap.c
src/inc.c
src/news.c
src/password.c [new file with mode: 0644]
src/password.h [new file with mode: 0644]
src/password_gtk.c [new file with mode: 0644]
src/password_gtk.h [new file with mode: 0644]
src/prefs_common.c
src/prefs_common.h
src/prefs_gtk.c
src/prefs_other.c
src/send_message.c
src/wizard.c

index 8bc3db4..4e38388 100644 (file)
@@ -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:"
index a3b2113..f958fd5 100644 (file)
@@ -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 \
index 60c8e31..8ccf871 100644 (file)
@@ -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;
index bfa5bf4..c4f82c2 100644 (file)
--- 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;
 
index fdcea33..ead70cb 100644 (file)
@@ -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 (file)
index 0000000..180f6f4
--- /dev/null
@@ -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 (file)
index 0000000..df18e34
--- /dev/null
@@ -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 (file)
index 0000000..b2fb6b8
--- /dev/null
@@ -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 (file)
index 0000000..86fa9d2
--- /dev/null
@@ -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 */
index 69f1e6a..50f9ebf 100644 (file)
@@ -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}
 };
index 6c98ae1..ed86cd0 100644 (file)
@@ -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;
index 3525d19..68840bf 100644 (file)
@@ -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);
index 83d5cf1..09a4b6c 100644 (file)
@@ -17,7 +17,7 @@
  */
 
 #ifdef HAVE_CONFIG_H
-#  include "config.h"
+#include "config.h"
 #include "claws-features.h"
 #endif
 
 #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
index 18f7f3a..07e3583 100644 (file)
@@ -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
index e2e9a91..a13a4f2 100644 (file)
@@ -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;
        }