From 991d0d96e5e079dae7feef4fc9c4736c306f6e57 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Sun, 30 Jun 2024 19:06:22 +0200 Subject: Fix legacy ebuilds and fix pkgcheck issues --- .../sylpheed/files/sylpheed-3.7.0-ALL.patch | 2149 -------------------- 1 file changed, 2149 deletions(-) delete mode 100644 mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch (limited to 'mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch') diff --git a/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch b/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch deleted file mode 100644 index 0a34542..0000000 --- a/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch +++ /dev/null @@ -1,2149 +0,0 @@ -diff --git a/.gitignore b/.gitignore -new file mode 100644 -index 0000000..9742351 ---- /dev/null -+++ b/.gitignore -@@ -0,0 +1,21 @@ -+*.o -+*.lo -+*.la -+Makefile -+config.h -+config.log -+config.status -+libsylph/.deps/ -+libsylph/.libs/ -+libtool -+plugin/attachment_tool/.deps/ -+plugin/attachment_tool/.libs/ -+plugin/test/.deps/ -+plugin/test/.libs/ -+po/Makefile.in -+po/POTFILES -+src/.deps/ -+src/.libs/ -+src/sylpheed -+src/sylpheed.rc -+stamp-h1 -diff --git a/README.md b/README.md -new file mode 100644 -index 0000000..1cf6d21 ---- /dev/null -+++ b/README.md -@@ -0,0 +1,219 @@ -+## Implemented features and fixes not present in the official Sylpheed release -+ -+- (fix) -+ PGP signature not verified properly when the message has no newline -+ at the end. https://sylpheed.sraoss.jp/redmine/issues/288 -+ -+- (feature) -+ Make it possible to select "Show signature check result in a popup window" -+ only for bad signatures. -+ -+- (feature) -+ Support for encrypting and storing encrypted passwords using a master password. -+ Read bellow for more details! -+ -+ -+## Master password -+ -+The master password feature is developed by Simeon Simeonov (sgs) -+and is currently in an experimental state. -+ -+ -+### Motivation -+ -+Currently Sylpheed is storing passwords in plain-text. One can always refrain -+from storing passwords and let Sylpheed prompt for them, but the more accounts -+one has, the more annoying this becomes. -+ -+The goal is to have the passwords stored in a secure way and let Sylpheed only -+prompt for the master password. -+ -+ -+### Security goals -+ -+- attacker (A) should not be able to derive the password from the digest. -+ -+- A should not be able to derive the master password even if she has -+ read and write access to the storage. -+ -+- A should not be able to determine the length of the encrypted password -+ even if she has read and write access to the storage. -+ -+- A should not be able to craft an edited password without obtaining the -+ master password. -+ -+- A should not be able to compromise the master password or force Sylpheed -+ to store decrypted passwords even if she has access to a running Sylpheed. -+ -+- a warning / prompt should be given if a user accidentally types in a "wrong" -+ master password before decryption is initiated. -+ -+ -+### Usage in Sylpheed -+ -+- backup your Sylpheed profile (often $HOME/.sylpheed-2.0)! -+ -+- start Sylpheed and open "Configuration" -> "Common preferences..."! -+ -+- select the "Master password" tab, enable "Use master password" and -+ apply the changes! -+ -+- restart Sylpheed (exit and then start Sylpheed again)! -+ -+- you will be asked to type and verify a new master password. -+ -+- set your new passwords from the "Configuration" -> "Edit accounts..."! -+ -+- select "Automatically unload master password after session initialization" -+ for increased security and decreased convenience. -+ -+Note: -+Sylpheed will automatically convert existing stored passwords, but it will not -+touch your backups. You will have to remove all remnants of plain-text -+passwords manually. -+ -+ -+### Choice of cryptographic primitives -+ -+The primary concern when selecting cryptographic primitives was portability. -+The desire was to go for primitives that are both strong and available in all -+supported production distributions of OpenSSL and LibreSSL. -+ -+ -+#### Cipher -+ -+When it comes to implementation, there are several advantages in using stream -+cipher or a block cipher that behaves like a stream cipher when used in a -+certain mode of operation. One is avoiding to deal with padding. -+ -+AES-256 operating in CFB was selected for these reasons. -+ChaCha20 should be considered as a replacement in the future. -+ -+ -+#### Hash-function -+ -+Hash-functions are used for: -+- key derivation -+- plain-text digest -+ -+Those operations do not have to use the same hash-function. -+(See the "Encryption & decryption scheme" section for more details!) -+ -+Key-derivation: -+Since AES-256 uses a 256 bits key, we need a hash-function with at least -+the same digest size or bigger. -+Since the digest (which is the key itself) is considered confidential and is -+stored only in memory for only a limited amount of time, SHA-256 is considered -+sufficiently strong for that purpose. -+ -+Size + plain-text + padding digest: -+Since the digest is created of both the plain-text and the plain-text size, -+as well as being encrypted, SHA-256 is considered sufficiently strong. -+SHA-512 may increase security at the price of adding additional 32 bytes -+to the encrypted password digest. -+ -+Stronger hash-functions like SHA-3 or BLAKE2b can be considered as a -+replacement in the future. -+ -+ -+#### Master password digest -+ -+In order to be able to decide whether the user typed a "wrong" master password, -+before attempting to decrypt, Sylpheed stores a digest of the master password -+in 'master_password_hash' in sylpheedrc. -+100000 iterations of PBKDF2_HMAC with SHA-512 and 16 bytes salt is used. -+Note that this digest is useless as a key and even if a plain-text that -+produces the same digest is found, it will most probably be useless as a -+master-password. -+ -+ -+### Encryption & decryption scheme -+ -+ -+#### Encryption -+ -+ -+Input: -+ -+- plain-text password to be encrypted (P) -+ -+- plain-text master-password used for key derivation (M) -+ -+- integer minimum password length (0 < L < 100) -+ -+ -+Output: -+ -+- an encrypted password digest (base64) (B) -+ -+ -+Operation: -+ -+- generate 16 bytes of random data to be used as a salt (S) -+ -+- derive the key (K): K = SHA_256(S + M) -+ -+- produce a 2 byte string (N) indicating the length of P -+ -+- generate random padding and set N: -+ - if the length of P < L, produce L - P bytes of random data (R), N = "%02d" -+ - if the length of P >= L, N = "-1" -+ -+- produce a hash digest (H): H = SHA_256(N + P + R (if the length of P < L)) -+ -+- encrypt (E): E = AES_256_CFB_ENCRYPT(H + N + P + R (if the length of P < L), K) -+ -+- B = mpes1:BASE64_ENCODE(S + E) -+ -+example: -+mpes1:vo7lsIpD7i6byBA6+vlUoF4OVDfEe+aYRRk4FRtfJ2gMY8M43Kj6WfdfgbViIOl83bI4XEc96okhPW5Mla813aAR1gbPjDg0xmCyIbWOiUv/dg== -+ -+ -+#### Decryption -+ -+ -+Input: -+ -+- encrypted password digest (base64) (B) -+ -+- plain-text master-password used for key derivation (M) -+ -+ -+Output: -+ -+- plain-text password (P) -+ -+ -+Operation: -+ -+- remove the prefix (mpes1:) and base64-decode the rest of the digest: B = BASE64_DECODE(B) -+ -+- fetch the first 16 bytes for the salt: S = B[0 : 15] -+ -+- derive the key (K): K = SHA_256(S + M) -+ -+- decrypt the rest of B (D): D = AES_256_CFB_DECRYPT(B[16 :], K) -+ -+- extract the first 16 bytes for the hash digest (H): H = D[0 : 15] -+ -+- in order to detect data-inconsistency, assert H == SHA_256(D[16 :]) -+ -+- extract the next 2 bytes for the length of P (N): N = D[16 : 17] -+ -+- fetch the plain-text: -+ - if N == "-1" the password is the remaining bytes of D: P = D[18 :] -+ - if N != "-1", extract the next N-bytes from D: P = D[18 : (18 + N)] -+ -+ -+### Limitations -+ -+- currently only the 'password' and 'smtp_password' keys in accountrc -+ are encrypted. -+ A mechanism that allows for any key and even folders to be encrypted -+ should be considered in the future. -+ -+- currently it is not possible to select alternative ciphers, hash-functions -+ and modes of operation (without editing the source code). -+ -+- currently it is not possible to change your master password without having to -+ (re)set your passwords manually. -diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am -index 8f2b76b..40d26a9 100644 ---- a/libsylph/Makefile.am -+++ b/libsylph/Makefile.am -@@ -19,6 +19,7 @@ libsylph_0_la_SOURCES = \ - folder.c \ - html.c \ - imap.c \ -+ masterpassword.c \ - mbox.c \ - md5.c \ - md5_hmac.c \ -@@ -62,6 +63,7 @@ libsylph_0include_HEADERS = \ - folder.h \ - html.h \ - imap.h \ -+ masterpassword.h \ - mbox.h \ - md5.h \ - md5_hmac.h \ -diff --git a/libsylph/Makefile.in b/libsylph/Makefile.in -index 6e3c999..ac8fefd 100644 ---- a/libsylph/Makefile.in -+++ b/libsylph/Makefile.in -@@ -129,8 +129,8 @@ libsylph_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) - am_libsylph_0_la_OBJECTS = account.lo base64.lo codeconv.lo \ - customheader.lo displayheader.lo filter.lo folder.lo html.lo \ -- imap.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo nntp.lo \ -- pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \ -+ imap.lo masterpassword.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo \ -+ nntp.lo pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \ - procmime.lo procmsg.lo quoted-printable.lo recv.lo session.lo \ - smtp.lo socket.lo socks.lo ssl.lo ssl_hostname_validation.lo \ - stringtable.lo sylmain.lo unmime.lo utils.lo uuencode.lo \ -@@ -402,6 +402,7 @@ libsylph_0_la_SOURCES = \ - folder.c \ - html.c \ - imap.c \ -+ masterpassword.c \ - mbox.c \ - md5.c \ - md5_hmac.c \ -@@ -445,6 +446,7 @@ libsylph_0include_HEADERS = \ - folder.h \ - html.h \ - imap.h \ -+ masterpassword.h \ - mbox.h \ - md5.h \ - md5_hmac.h \ -@@ -579,6 +581,7 @@ distclean-compile: - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Plo@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterpassword.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_hmac.Plo@am__quote@ -diff --git a/libsylph/defs.h b/libsylph/defs.h -index 9e3f82b..67325bf 100644 ---- a/libsylph/defs.h -+++ b/libsylph/defs.h -@@ -58,6 +58,9 @@ - #define DISPLAY_HEADER_RC "dispheaderrc" - #define MENU_RC "menurc" - #define ACTIONS_RC "actionsrc" -+#ifdef USE_SSL -+#define SECURE_RC "securerc" -+#endif - #define COMMAND_HISTORY "command_history" - #define TEMPLATE_DIR "templates" - #define TMP_DIR "tmp" -diff --git a/libsylph/imap.c b/libsylph/imap.c -index aa0d737..e1c7cfd 100644 ---- a/libsylph/imap.c -+++ b/libsylph/imap.c -@@ -52,6 +52,7 @@ - #include "utils.h" - #include "prefs_common.h" - #include "virtual.h" -+#include "masterpassword.h" - - #define IMAP4_PORT 143 - #if USE_SSL -@@ -705,9 +706,13 @@ static gint imap_session_connect(IMAPSession *session) - account = (PrefsAccount *)(SESSION(session)->data); - - log_message(_("creating IMAP4 connection to %s:%d ...\n"), -- SESSION(session)->server, SESSION(session)->port); -- -- pass = account->passwd; -+ SESSION(session)->server, SESSION(session)->port); -+ if (master_password_active()) { -+ pass = decrypt_with_master_password(account->passwd); -+ /* a new string is allocated. To be removed ... */ -+ } else { -+ pass = account->passwd; -+ } - if (!pass) - pass = account->tmp_pass; - if (!pass) { -@@ -770,8 +775,11 @@ static gint imap_session_connect(IMAPSession *session) - #endif - - if (!session->authenticated && -- imap_auth(session, account->userid, pass, account->imap_auth_type) -- != IMAP_SUCCESS) { -+ imap_auth(session, account->userid, pass, account->imap_auth_type) -+ != IMAP_SUCCESS) { -+ if (master_password_active()) { -+ g_free(pass); /* remove the decrypted password */ -+ } - if (account->tmp_pass) { - g_free(account->tmp_pass); - account->tmp_pass = NULL; -@@ -780,6 +788,10 @@ static gint imap_session_connect(IMAPSession *session) - return IMAP_AUTHFAIL; - } - -+ if (master_password_active()) { -+ g_free(pass); /* remove the decrypted password */ -+ } -+ - return IMAP_SUCCESS; - } - -diff --git a/libsylph/masterpassword.c b/libsylph/masterpassword.c -new file mode 100644 -index 0000000..3d7af94 ---- /dev/null -+++ b/libsylph/masterpassword.c -@@ -0,0 +1,216 @@ -+/* -+ * LibSylph -- E-Mail client library -+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+ -+#include "prefs_common.h" -+#include "ssl.h" -+#include "utils.h" -+#include "masterpassword.h" -+ -+ -+gchar *master_password; -+gboolean master_password_enabled_on_init; -+ -+void set_master_password(const char *password) { -+ master_password = password; -+} -+ -+gchar *get_master_password(void) { -+ return master_password; -+} -+ -+void cleanse_buffer(void *buf, size_t len) { -+#if USE_SSL -+ OPENSSL_cleanse(buf, len); -+#else -+ memset(buf, 0, len); /* better than nothing */ -+#endif -+} -+ -+void unload_master_password(void) { -+ -+ debug_print("Unloading master password\n"); -+ if (master_password == NULL) { -+ /* not loaded / already unloaded */ -+ return; -+ } -+ cleanse_buffer(master_password, strlen(master_password)); -+ g_free(master_password); -+ master_password = NULL; -+ debug_print("Master password unloaded\n"); -+ -+} -+ -+gint mpes_string_prefix(const gchar *str) { -+ -+ /* this function will be expanded in time as the format changes */ -+ if (g_str_has_prefix(str, "mpes1:")) -+ return 6; -+ return 0; -+ -+} -+ -+gboolean master_password_active(void) { -+ -+#if USE_SSL -+ return ((master_password != NULL) && -+ prefs_common.use_master_password); -+#else -+ return FALSE; -+#endif -+ -+} -+ -+gchar *decrypt_with_master_password(const gchar *str) { -+ -+#if USE_SSL -+ gchar *new_str; -+ gint str_prefix; -+ -+ if ((!str) || (!prefs_common.use_master_password)) -+ return g_strdup(str); -+ -+ if (master_password == NULL) { -+ /* we have empty or auto unloaded master password */ -+ if ((!prefs_common.auto_unload_master_password) || -+ (check_master_password_interactively(3) != MP_RC_OK)) { -+ return g_strdup(str); -+ } -+ debug_print("Reloaded master password\n"); -+ } -+ -+ str_prefix = mpes_string_prefix(str); -+ if (!str_prefix) -+ return g_strdup(str); -+ -+ if (decrypt_data(&new_str, -+ str + str_prefix, -+ master_password, -+ strlen(str) + 1 - str_prefix) != MP_RC_OK) { -+ OPENSSL_cleanse(new_str, strlen(new_str)); -+ g_free(new_str); -+ return g_strdup(str); -+ } -+ -+ return new_str; -+#else -+ return g_strdup(str); -+#endif -+ -+} -+ -+gchar *encrypt_with_master_password(const gchar *str) { -+ -+#if USE_SSL -+ gchar *new_str, *mpes1_str; -+ gint length_encrypted; -+ -+ if ((!str) || (!master_password_active())) -+ return NULL; -+ -+ /* -+ * unlike the decrypt function, here it is up to the caller -+ * to make sure that auto unloaded master password is handled properly -+ */ -+ -+ if (encrypt_data(&new_str, -+ &length_encrypted, -+ str, -+ master_password, -+ strlen(str) + 1, -+ prefs_common.encrypted_password_min_length, -+ TRUE) != MP_RC_OK) { -+ g_free(new_str); -+ return NULL; -+ } -+ -+ mpes1_str = g_strdup_printf("mpes1:%s", new_str); -+ g_free(new_str); -+ -+ return mpes1_str; -+#else -+ return NULL; -+#endif -+ -+} -+ -+#if USE_SSL -+gint set_master_password_interactively(guint max_attempts) { -+ -+ if (master_password == NULL) -+ master_password = input_set_new_password(max_attempts); -+ -+ if (master_password == NULL) -+ return 1; -+ -+ if (generate_password_hash( -+ &prefs_common.master_password_hash, -+ master_password, -+ NULL) != MP_RC_OK) { -+ /* should not really happen unless buggy code / library */ -+ g_free(prefs_common.master_password_hash); -+ prefs_common.master_password_hash = NULL; -+ debug_print(_("Could not generate master password hash")); -+ return 1; -+ } -+ -+ prefs_common_write_config(); -+ return 0; -+ -+} -+ -+gint check_master_password_interactively(guint max_attempts) { -+ -+ guint cnt; -+ -+ g_return_val_if_fail(max_attempts > 0, 1); -+ g_return_val_if_fail(prefs_common.master_password_hash != NULL, 1); -+ -+ if (master_password != NULL) { -+ /* password already cached */ -+ debug_print("Master password already cached\n"); -+ return check_password(master_password, -+ prefs_common.master_password_hash); -+ } -+ -+ for (cnt = 0; cnt < max_attempts; ++cnt) { -+ master_password = input_query_master_password(); -+ if (master_password == NULL) { -+ /* input canceled or query_master_password_func not set */ -+ continue; -+ } -+ if (check_password(master_password, -+ prefs_common.master_password_hash) == MP_RC_OK) { -+ return MP_RC_OK; /* match */ -+ } -+ debug_print(_("Wrong master password entered (%d)\n"), cnt); -+ OPENSSL_cleanse(master_password, strlen(master_password)); -+ g_free(master_password); -+ master_password = NULL; -+ } -+ -+ return 1; /* no match */ -+ -+} -+#endif /* USE_SSL */ -diff --git a/libsylph/masterpassword.h b/libsylph/masterpassword.h -new file mode 100644 -index 0000000..7254643 ---- /dev/null -+++ b/libsylph/masterpassword.h -@@ -0,0 +1,47 @@ -+/* -+ * LibSylph -- E-Mail client library -+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+#ifndef __MASTERPASSWORD_H__ -+#define __MASTERPASSWORD_H__ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#define MP_RC_OK 0 -+#define MP_RC_WRONG_HASH_OR_KEY 1 -+#define MP_RC_INVALID_FORMAT 2 /* invalid digest format */ -+ -+extern gchar *master_password; -+extern gboolean master_password_enabled_on_init; /* m.p. enabled on init? */ -+void set_master_password(const char *password); -+gchar *get_master_password(void); -+void cleanse_buffer(void *buf, size_t len); -+void unload_master_password(void); -+gint mpes_string_prefix(const gchar *str); -+gboolean master_password_active(void); -+gchar *decrypt_with_master_password(const gchar *str); -+gchar *encrypt_with_master_password(const gchar *str); -+ -+#if USE_SSL -+gint set_master_password_interactively(guint max_attempts); -+gint check_master_password_interactively(guint max_attempts); -+#endif /* USE_SSL */ -+ -+#endif /* __MASTERPASSWORD_H__ */ -diff --git a/libsylph/news.c b/libsylph/news.c -index ffff9f9..36d3b10 100644 ---- a/libsylph/news.c -+++ b/libsylph/news.c -@@ -44,6 +44,7 @@ - #include "utils.h" - #include "prefs_common.h" - #include "prefs_account.h" -+#include "masterpassword.h" - #if USE_SSL - # include "ssl.h" - #endif -@@ -249,10 +250,11 @@ static Session *news_session_new_for_folder(Folder *folder) - ac = folder->account; - if (ac->use_nntp_auth && ac->userid && ac->userid[0]) { - userid = ac->userid; -- if (ac->passwd && ac->passwd[0]) -- passwd = g_strdup(ac->passwd); -- else -+ if (ac->passwd && ac->passwd[0]) { -+ passwd = decrypt_with_master_password(ac->passwd); -+ } else { - passwd = input_query_password(ac->nntp_server, userid); -+ } - } - - if (ac->use_socks && ac->use_socks_for_recv && ac->proxy_host) { -diff --git a/libsylph/pop.c b/libsylph/pop.c -index 8cb7f5c..85f3ed9 100644 ---- a/libsylph/pop.c -+++ b/libsylph/pop.c -@@ -39,6 +39,7 @@ - #include "prefs_account.h" - #include "utils.h" - #include "recv.h" -+#include "masterpassword.h" - - gint pop3_greeting_recv (Pop3Session *session, - const gchar *msg); -@@ -437,8 +438,9 @@ Session *pop3_session_new(PrefsAccount *account) - session->error_msg = NULL; - - session->user = g_strdup(account->userid); -- session->pass = account->passwd ? g_strdup(account->passwd) : -- account->tmp_pass ? g_strdup(account->tmp_pass) : NULL; -+ session->pass = account->passwd ? decrypt_with_master_password( -+ account->passwd) : account->tmp_pass ? g_strdup( -+ account->tmp_pass) : NULL; - - SESSION(session)->server = g_strdup(account->recv_server); - -diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c -index 1aecba9..54cbc89 100644 ---- a/libsylph/prefs_account.c -+++ b/libsylph/prefs_account.c -@@ -34,6 +34,7 @@ - #include "customheader.h" - #include "account.h" - #include "utils.h" -+#include "masterpassword.h" - - static PrefsAccount tmp_ac_prefs; - -@@ -205,7 +206,7 @@ PrefsAccount *prefs_account_new(void) - void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label) - { - const gchar *p = label; -- gchar *rcpath; -+ gchar *rcpath, *tmp_str; - gint id; - - g_return_if_fail(ac_prefs != NULL); -@@ -229,6 +230,34 @@ void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label) - ac_prefs->use_apop_auth = TRUE; - } - -+ if (master_password_active()) { -+ if ((ac_prefs->passwd != NULL) && -+ !mpes_string_prefix(ac_prefs->passwd)) { -+ /* TODO: Perhaps some prompt? */ -+ debug_print( -+ "%s -> converting passwd cleartext to encrypted\n", -+ label); -+ tmp_str = ac_prefs->passwd; -+ ac_prefs->passwd = encrypt_with_master_password(ac_prefs->passwd); -+ cleanse_buffer(tmp_str, strlen(tmp_str)); -+ g_free(tmp_str); -+ } -+ -+ if ((ac_prefs->smtp_passwd != NULL) && -+ !mpes_string_prefix(ac_prefs->smtp_passwd)) { -+ /* TODO: Perhaps some prompt? */ -+ debug_print( -+ "%s -> converting smtp_passwd from cleartext to encrypted\n", -+ label); -+ tmp_str = ac_prefs->smtp_passwd; -+ ac_prefs->smtp_passwd = encrypt_with_master_password( -+ ac_prefs->smtp_passwd); -+ cleanse_buffer(tmp_str, strlen(tmp_str)); -+ g_free(tmp_str); -+ } -+ -+ } -+ - custom_header_read_config(ac_prefs); - } - -diff --git a/libsylph/prefs_common.c b/libsylph/prefs_common.c -index 8ed74a3..192964e 100644 ---- a/libsylph/prefs_common.c -+++ b/libsylph/prefs_common.c -@@ -406,6 +406,8 @@ static PrefParam param[] = { - P_BOOL}, - {"gpg_signature_popup", "FALSE", &prefs_common.gpg_signature_popup, - P_BOOL}, -+ {"gpg_signature_popup_mode", "1", &prefs_common.gpg_signature_popup_mode, -+ P_INT}, - {"store_passphrase", "FALSE", &prefs_common.store_passphrase, P_BOOL}, - {"store_passphrase_timeout", "0", - &prefs_common.store_passphrase_timeout, P_INT}, -@@ -416,6 +418,17 @@ static PrefParam param[] = { - {"show_gpg_warning", "TRUE", &prefs_common.gpg_warning, P_BOOL}, - #endif - -+ /* Master password */ -+ {"use_master_password", "FALSE", &prefs_common.use_master_password, -+ P_BOOL}, -+ {"master_password_hash", NULL, &prefs_common.master_password_hash, -+ P_STRING}, -+ {"encrypted_password_min_length", "32", -+ &prefs_common.encrypted_password_min_length, P_INT}, -+ {"auto_unload_master_password", "FALSE", -+ &prefs_common.auto_unload_master_password, -+ P_BOOL}, -+ - /* Interface */ - {"separate_folder", "FALSE", &prefs_common.sep_folder, P_BOOL}, - {"separate_message", "FALSE", &prefs_common.sep_msg, P_BOOL}, -diff --git a/libsylph/prefs_common.h b/libsylph/prefs_common.h -index ba9ddb5..6f9c364 100644 ---- a/libsylph/prefs_common.h -+++ b/libsylph/prefs_common.h -@@ -243,11 +243,18 @@ struct _PrefsCommon - /* Privacy */ - gboolean auto_check_signatures; - gboolean gpg_signature_popup; -+ gint gpg_signature_popup_mode; - gboolean store_passphrase; - gint store_passphrase_timeout; - gboolean passphrase_grab; - gboolean gpg_warning; - -+ /* Master password */ -+ gboolean use_master_password; -+ gchar *master_password_hash; -+ guint encrypted_password_min_length; -+ gboolean auto_unload_master_password; -+ - /* Interface */ - gboolean sep_folder; - gboolean sep_msg; -diff --git a/libsylph/ssl.c b/libsylph/ssl.c -index 8413925..e782b0e 100644 ---- a/libsylph/ssl.c -+++ b/libsylph/ssl.c -@@ -32,6 +32,13 @@ - #include "ssl.h" - #include "ssl_hostname_validation.h" - -+#define SALT_SIZE 16 -+#define CIPHER EVP_aes_256_cfb() -+#define KEY_HASH EVP_sha256() -+#define DIGEST_HASH EVP_sha256() -+#define PBKDF2_DIGEST_SIZE 64 -+#define PBKDF2_ITERATIONS 100000 -+ - static SSL_CTX *ssl_ctx_SSLv23 = NULL; - static SSL_CTX *ssl_ctx_TLSv1 = NULL; - -@@ -402,4 +409,468 @@ void ssl_set_verify_func(SSLVerifyFunc func) - verify_ui_func = func; - } - -+/* master password related functions */ -+static gint secure_derive_key(guchar *key, -+ gint length_key, -+ const gchar *passphrase, -+ const guchar *salt) { -+ -+ guint length_buffer, length_hash; -+ guchar *buffer, *ptr_hash; -+ -+ EVP_MD_CTX *mdctx; -+ -+ OPENSSL_cleanse(key, length_key); -+ -+ length_buffer = SALT_SIZE + strlen(passphrase); -+ buffer = OPENSSL_malloc(length_buffer); -+ OPENSSL_cleanse(buffer, length_buffer); -+ -+ memcpy(buffer, salt, SALT_SIZE); -+ memcpy(buffer + SALT_SIZE, passphrase, strlen(passphrase)); -+ -+ mdctx = EVP_MD_CTX_create(); -+ EVP_DigestInit_ex(mdctx, KEY_HASH, NULL); -+ EVP_DigestUpdate(mdctx, buffer, length_buffer); -+ OPENSSL_cleanse(buffer, length_buffer); -+ -+ ptr_hash = OPENSSL_malloc(EVP_MD_size(KEY_HASH)); -+ EVP_DigestFinal_ex(mdctx, ptr_hash, &length_hash); -+ -+ memcpy(key, -+ ptr_hash, -+ (length_hash > length_key) ? length_key : length_hash); -+ -+ OPENSSL_cleanse(ptr_hash, length_hash); -+ OPENSSL_free(ptr_hash); -+ OPENSSL_free(buffer); -+ EVP_MD_CTX_destroy(mdctx); -+ -+ return SSL_RC_OK; -+ -+} -+ -+gint encrypt_data(gchar **encrypted, -+ gint *length_encrypted, -+ const gchar *data, -+ const gchar *passphrase, -+ gint length_data, -+ guint min_data_length, -+ gboolean rnd_salt) { -+ -+ gint crypt_buffer_cnt, rc; -+ guint key_size, length_hash; -+ guint length_cleartext, length_ciphertext, length_total; -+ guchar salt[SALT_SIZE]; -+ guchar *ciphertext_buffer, *total_buffer; -+ /* sensitive buffers and counters */ -+ guint length_data_payload, length_padding; -+ gchar str_data_size[3]; -+ guchar *data_payload_buffer, *hash_buffer, *padding_buffer, *key; -+ guchar *cleartext_buffer; -+ -+ EVP_CIPHER_CTX *ctx; -+ EVP_MD_CTX *mdctx; -+ -+ rc = SSL_RC_ERROR; -+ -+ if (length_data < 1) { -+ return -1; -+ } -+ if (rnd_salt) { -+ if (RAND_bytes(salt, SALT_SIZE) != 1) { -+ debug_print("Random problems...\n"); -+ goto cleanup; -+ } -+ } else { -+ strncpy((gchar *)salt, "FOR TESTING ONLY", SALT_SIZE); -+ } -+ -+ key_size = EVP_CIPHER_key_length(CIPHER); -+ key = OPENSSL_malloc(key_size); -+ OPENSSL_cleanse(key, key_size); -+ if (secure_derive_key(key, -+ key_size, -+ passphrase, -+ salt) != SSL_RC_OK) { -+ OPENSSL_cleanse(key, key_size); -+ debug_print("Could not generate secure key\n"); -+ goto cleanup; -+ } -+ -+ /* prepare the data-buffer */ -+ length_padding = 0; -+ if (length_data < min_data_length) { -+ g_snprintf(str_data_size, 3, "%02d", length_data); -+ length_padding = min_data_length - length_data; -+ padding_buffer = OPENSSL_malloc(length_padding); -+ OPENSSL_cleanse(padding_buffer, length_padding); -+ if (RAND_bytes(padding_buffer, length_padding) != 1) { -+ debug_print("Random problems...\n"); -+ goto cleanup; -+ } -+ } else { -+ g_snprintf(str_data_size, 3, "-1"); -+ } -+ -+ length_data_payload = 2 + length_data + length_padding; -+ data_payload_buffer = OPENSSL_malloc(length_data_payload); -+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); -+ -+ memcpy(data_payload_buffer, str_data_size, 2); -+ memcpy(data_payload_buffer + 2, data, length_data); -+ if (length_padding > 0) { -+ memcpy(data_payload_buffer + (2 + length_data), -+ padding_buffer, -+ length_padding); -+ } -+ -+ mdctx = EVP_MD_CTX_create(); -+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL); -+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload); -+ -+ length_hash = EVP_MD_size(DIGEST_HASH); -+ hash_buffer = OPENSSL_malloc(length_hash); -+ OPENSSL_cleanse(hash_buffer, length_hash); -+ EVP_DigestFinal_ex(mdctx, hash_buffer, NULL); -+ -+ length_cleartext = length_hash + length_data_payload; -+ -+ cleartext_buffer = OPENSSL_malloc(length_cleartext); -+ OPENSSL_cleanse(cleartext_buffer, length_cleartext); -+ -+ /* assemble cleartext-buffer */ -+ memcpy(cleartext_buffer, hash_buffer, length_hash); -+ memcpy(cleartext_buffer + length_hash, -+ data_payload_buffer, -+ length_data_payload); -+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); /* sensitive */ -+ -+ /* encryption */ -+ if (!(ctx = EVP_CIPHER_CTX_new())) { -+ debug_print("New ctx failed\n"); -+ goto cleanup; -+ } -+ -+ length_ciphertext = 0; -+ if (EVP_EncryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) { -+ debug_print("EVP_EncryptInit_ex failed\n"); -+ goto cleanup; -+ } -+ -+ ciphertext_buffer = OPENSSL_malloc(length_cleartext); -+ -+ crypt_buffer_cnt = 0; -+ while(1) { -+ if (EVP_EncryptUpdate(ctx, -+ ciphertext_buffer + length_ciphertext, -+ &crypt_buffer_cnt, -+ cleartext_buffer + length_ciphertext, -+ 1) != 1) { /* one byte at a time */ -+ debug_print("EVP_EncryptUpdate failed\n"); -+ goto cleanup; -+ } -+ -+ if (crypt_buffer_cnt != 1) { /* paranoia */ -+ debug_print("The sizes of enc and dec text do not correspond\n"); -+ goto cleanup; -+ } -+ -+ ++length_ciphertext; -+ -+ if (length_ciphertext >= length_cleartext) { -+ break; -+ } -+ } -+ /* No padding required for the CFB mode */ -+ -+ OPENSSL_cleanse(cleartext_buffer, length_cleartext); /* sensitive */ -+ length_total = SALT_SIZE + length_ciphertext; -+ total_buffer = OPENSSL_malloc(length_total); -+ -+ memcpy(total_buffer, salt, SALT_SIZE); -+ memcpy(total_buffer + SALT_SIZE, ciphertext_buffer, length_ciphertext); -+ -+ *encrypted = g_base64_encode(total_buffer, length_total); -+ *length_encrypted = strlen(*encrypted); -+ -+ rc = SSL_RC_OK; -+ -+cleanup: -+ /* key */ -+ OPENSSL_cleanse(key, key_size); -+ OPENSSL_free(key); -+ /* cleartext buffer */ -+ OPENSSL_cleanse(cleartext_buffer, length_cleartext); -+ OPENSSL_free(cleartext_buffer); -+ /* payload buffer */ -+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); -+ OPENSSL_free(data_payload_buffer); -+ /* hash */ -+ OPENSSL_cleanse(hash_buffer, length_hash); -+ OPENSSL_free(hash_buffer); -+ /* padding */ -+ if (length_padding > 0) { -+ OPENSSL_cleanse(padding_buffer, length_padding); -+ OPENSSL_free(padding_buffer); -+ } -+ -+ OPENSSL_cleanse(str_data_size, 3); /* paranoia */ -+ -+ /* ciphertext buffer */ -+ OPENSSL_free(ciphertext_buffer); -+ -+ /* total buffer */ -+ OPENSSL_free(total_buffer); -+ -+ length_data_payload = 0; -+ length_padding = 0; -+ -+ EVP_CIPHER_CTX_free(ctx); -+ EVP_MD_CTX_destroy(mdctx); -+ -+ return rc; -+ -+} -+ -+gint decrypt_data(gchar **decrypted, -+ const gchar *data, -+ const gchar *passphrase, -+ gint length_data) { -+ -+ gint rc; /* return code */ -+ gint decrypt_buffer_cnt, length_ciphertext, length_decrypted; -+ guint key_size, length_hash, length_cleartext; -+ gsize length_total; -+ guchar salt[SALT_SIZE]; -+ guchar *ciphertext_buffer, *total_buffer; -+ /* sensitive buffers and counters */ -+ gint data_size; -+ guint length_data_payload; -+ gchar str_data_size[3]; -+ guchar *data_payload_buffer, *hash_buffer, *key; -+ guchar *cleartext_buffer; -+ -+ EVP_CIPHER_CTX *ctx; -+ EVP_MD_CTX *mdctx; -+ -+ rc = -1; -+ -+ if (length_data < 1) { -+ return -1; -+ } -+ -+ total_buffer = g_base64_decode(data, &length_total); -+ length_ciphertext = length_total - SALT_SIZE; -+ ciphertext_buffer = OPENSSL_malloc(length_ciphertext); -+ OPENSSL_cleanse(ciphertext_buffer, length_ciphertext); -+ -+ memcpy(salt, total_buffer, SALT_SIZE); -+ memcpy(ciphertext_buffer, total_buffer + SALT_SIZE, length_ciphertext); -+ -+ key_size = EVP_CIPHER_key_length(CIPHER); -+ -+ /* decryption */ -+ if(!(ctx = EVP_CIPHER_CTX_new())) { -+ debug_print("New ctx failed\n"); -+ goto cleanup; -+ } -+ -+ key = OPENSSL_malloc(key_size); -+ OPENSSL_cleanse(key, key_size); -+ if (secure_derive_key(key, -+ key_size, -+ passphrase, -+ salt) != 0) { -+ OPENSSL_cleanse(key, key_size); -+ debug_print("Could not generate secure key\n"); -+ goto cleanup; -+ } -+ -+ if (EVP_DecryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) { -+ debug_print("EVP_DecryptInit_ex failed\n"); -+ goto cleanup; -+ } -+ OPENSSL_cleanse(key, key_size); /* highly sensitive */ -+ cleartext_buffer = OPENSSL_malloc(length_ciphertext); -+ OPENSSL_cleanse(cleartext_buffer, length_ciphertext); -+ -+ length_cleartext = 0; -+ decrypt_buffer_cnt = 0; -+ -+ while(1) { -+ if (EVP_DecryptUpdate(ctx, -+ cleartext_buffer + length_cleartext, -+ &decrypt_buffer_cnt, -+ ciphertext_buffer + length_cleartext, -+ 1) != 1) { /* one byte at a time */ -+ debug_print("EVP_EncryptUpdate failed\n"); -+ goto cleanup; -+ } -+ -+ if (decrypt_buffer_cnt != 1) { /* paranoia */ -+ debug_print("The sizes of enc and dec text do not correspond"); -+ goto cleanup; -+ } -+ -+ ++length_cleartext; -+ -+ if (length_cleartext >= length_ciphertext) { -+ break; -+ } -+ } -+ -+ -+ length_hash = EVP_MD_size(DIGEST_HASH); -+ hash_buffer = OPENSSL_malloc(length_hash); -+ length_data_payload = length_cleartext - length_hash; -+ data_payload_buffer = OPENSSL_malloc(length_data_payload); -+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); -+ -+ memcpy(data_payload_buffer, -+ cleartext_buffer + length_hash, -+ length_data_payload); -+ -+ mdctx = EVP_MD_CTX_create(); -+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL); -+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload); -+ EVP_DigestFinal_ex(mdctx, hash_buffer, &length_hash); -+ -+ if (strncmp((const gchar*) hash_buffer, -+ (const gchar*) cleartext_buffer, -+ length_hash) != 0) { -+ debug_print("Invalid hash\n"); -+ rc = SSL_RC_WRONG_HASH_OR_KEY; -+ goto cleanup; -+ } -+ -+ memcpy(str_data_size, cleartext_buffer + length_hash, 2); -+ data_size = atoi(str_data_size); -+ -+ if (data_size < 0) { -+ length_decrypted = length_data_payload - 2; -+ } else { -+ length_decrypted = data_size; -+ } -+ *decrypted = OPENSSL_malloc(length_decrypted); -+ memcpy(*decrypted, data_payload_buffer + 2, length_decrypted); -+ -+ rc = SSL_RC_OK; -+ -+cleanup: -+ -+ /* key */ -+ OPENSSL_cleanse(key, key_size); -+ OPENSSL_free(key); -+ /* payload buffer */ -+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); -+ OPENSSL_free(data_payload_buffer); -+ /* hash */ -+ OPENSSL_cleanse(hash_buffer, length_hash); -+ OPENSSL_free(hash_buffer); -+ /* ciphertext */ -+ OPENSSL_free(ciphertext_buffer); -+ OPENSSL_free(total_buffer); -+ -+ EVP_CIPHER_CTX_free(ctx); -+ EVP_MD_CTX_destroy(mdctx); -+ -+ return rc; -+ -+} -+ -+gint generate_password_hash(gchar **password_hash, -+ const gchar *password, -+ const guchar *salt) { -+ /* -+ * Hashes 'password' using PKCS5_PBKDF2_HMAC with SHA512 and 'salt', -+ * and assigns a string to 'password_hash' with the following format: -+ * pbkdf2_sha512$iterations$base64(salt)$base64(password_hash) -+ */ -+ guchar lsalt[SALT_SIZE]; -+ gchar *PBKDF2_digest, *salt_b64, *digest_b64; -+ -+ if (salt == NULL) { -+ if (RAND_bytes(lsalt, SALT_SIZE) != 1) { -+ debug_print("Random problems...\n"); -+ return SSL_RC_ERROR; -+ } -+ } else { -+ memcpy(lsalt, salt, SALT_SIZE); -+ } -+ -+ PBKDF2_digest = OPENSSL_malloc(PBKDF2_DIGEST_SIZE); -+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE); -+ -+ PKCS5_PBKDF2_HMAC(password, -+ strlen(password), -+ lsalt, -+ SALT_SIZE, -+ PBKDF2_ITERATIONS, -+ EVP_sha512(), -+ PBKDF2_DIGEST_SIZE, -+ (guchar *) PBKDF2_digest); -+ digest_b64 = g_base64_encode((guchar *) PBKDF2_digest, PBKDF2_DIGEST_SIZE); -+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE); -+ OPENSSL_free(PBKDF2_digest); -+ -+ salt_b64 = g_base64_encode(lsalt, SALT_SIZE); -+ -+ *password_hash = g_strdup_printf("pbkdf2_sha512$%d$%s$%s", -+ PBKDF2_ITERATIONS, -+ salt_b64, -+ digest_b64); -+ -+ OPENSSL_cleanse(digest_b64, strlen(digest_b64)); -+ OPENSSL_free(digest_b64); -+ -+ OPENSSL_cleanse(salt_b64, strlen(salt_b64)); -+ OPENSSL_free(salt_b64); -+ -+ return SSL_RC_OK; -+ -+} -+ -+gint check_password(const gchar *password, const gchar *password_hash) { -+ -+ gint token_counter, rc; -+ guchar *salt; -+ gchar **tokens, *new_hash; -+ gsize salt_length; -+ -+ rc = SSL_RC_ERROR; -+ tokens = g_strsplit(password_hash, -+ "$", -+ -1); -+ token_counter = 0; -+ while (*(tokens + token_counter) != NULL) { -+ ++token_counter; -+ } -+ -+ if (token_counter != 4) { -+ debug_print("Invalid password hash...\n"); -+ goto cleanup; -+ } -+ -+ salt = g_base64_decode(*(tokens + 2), &salt_length); -+ if (salt_length != SALT_SIZE) { -+ debug_print("Salt size does not match\n"); -+ goto cleanup; -+ } -+ -+ if (generate_password_hash(&new_hash, password, salt) != SSL_RC_OK) { -+ debug_print("Password hash generation failed\n"); -+ goto cleanup; -+ } -+ -+ rc = g_strcmp0(password_hash, new_hash); -+ -+cleanup: -+ g_free(salt); -+ g_free(new_hash); -+ g_strfreev(tokens); -+ return rc; -+ -+} -+ - #endif /* USE_SSL */ -diff --git a/libsylph/ssl.h b/libsylph/ssl.h -index a9f690d..4c0ccd1 100644 ---- a/libsylph/ssl.h -+++ b/libsylph/ssl.h -@@ -32,9 +32,15 @@ - #include - #include - #include -+#include -+#include - - #include "socket.h" - -+#define SSL_RC_OK 0 -+#define SSL_RC_ERROR -1 -+#define SSL_RC_WRONG_HASH_OR_KEY 1 -+ - typedef enum { - SSL_METHOD_SSLv23, - SSL_METHOD_TLSv1 -@@ -60,6 +66,26 @@ void ssl_done_socket (SockInfo *sockinfo); - - void ssl_set_verify_func (SSLVerifyFunc func); - -+/* master password related code */ -+gint encrypt_data(gchar **encrypted, -+ gint *length_encrypted, -+ const gchar *data, -+ const gchar *passphrase, -+ gint length_data, -+ guint min_data_length, -+ gboolean rnd_salt); -+ -+gint decrypt_data(gchar **decrypted, -+ const gchar *data, -+ const gchar *passphrase, -+ gint length_data); -+ -+gint generate_password_hash(gchar **password_hash, -+ const gchar *password, -+ const guchar *salt); -+ -+gint check_password(const gchar *password, const gchar *password_hash); -+/* ---------------------------- */ - #endif /* USE_SSL */ - - #endif /* __SSL_H__ */ -diff --git a/libsylph/utils.c b/libsylph/utils.c -index aabce06..3bbcbcf 100644 ---- a/libsylph/utils.c -+++ b/libsylph/utils.c -@@ -3306,10 +3306,10 @@ gint canonicalize_file(const gchar *src, const gchar *dest) - } - } - -- if (last_linebreak == TRUE) { -- if (fputs("\r\n", dest_fp) == EOF) -- err = TRUE; -- } -+ /* if (last_linebreak == TRUE) { */ -+ /* if (fputs("\r\n", dest_fp) == EOF) */ -+ /* err = TRUE; */ -+ /* } */ - - if (ferror(src_fp)) { - FILE_OP_ERROR(src, "fgets"); -@@ -4598,6 +4598,36 @@ gchar *input_query_password(const gchar *server, const gchar *user) - return NULL; - } - -+static QueryMasterPasswordFunc query_master_password_func = NULL; -+ -+void set_input_query_master_password_func(QueryMasterPasswordFunc func) -+{ -+ query_master_password_func = func; -+} -+ -+gchar *input_query_master_password(void) -+{ -+ if (query_master_password_func) -+ return query_master_password_func(); -+ else -+ return NULL; -+} -+ -+static SetNewPasswordFunc set_new_password_func = NULL; -+ -+void set_input_set_new_password_func(SetNewPasswordFunc func) -+{ -+ set_new_password_func = func; -+} -+ -+gchar *input_set_new_password(guint max_attempts) -+{ -+ if (set_new_password_func) -+ return set_new_password_func(max_attempts); -+ else -+ return NULL; -+} -+ - /* logging */ - - static FILE *log_fp = NULL; -diff --git a/libsylph/utils.h b/libsylph/utils.h -index 9ac65cf..ede55ff 100644 ---- a/libsylph/utils.h -+++ b/libsylph/utils.h -@@ -202,6 +202,8 @@ typedef void (*ProgressFunc) (gint cur, - gint total); - typedef gchar * (*QueryPasswordFunc) (const gchar *server, - const gchar *user); -+typedef gchar * (*QueryMasterPasswordFunc) (void); -+typedef gchar * (*SetNewPasswordFunc) (guint max_attempts); - typedef void (*LogFunc) (const gchar *str); - typedef void (*LogFlushFunc) (void); - -@@ -560,9 +562,12 @@ void progress_show (gint cur, - - /* user input */ - void set_input_query_password_func (QueryPasswordFunc func); -- - gchar *input_query_password (const gchar *server, - const gchar *user); -+void set_input_query_master_password_func(QueryMasterPasswordFunc func); -+gchar *input_query_master_password(void); -+void set_input_set_new_password_func(SetNewPasswordFunc func); -+gchar *input_set_new_password(guint max_attempts); - - /* logging */ - void set_log_file (const gchar *filename); -diff --git a/src/inputdialog.c b/src/inputdialog.c -index a601085..cdfb997 100644 ---- a/src/inputdialog.c -+++ b/src/inputdialog.c -@@ -44,6 +44,7 @@ - #include "filesel.h" - #include "prefs_common.h" - #include "gtkutils.h" -+#include "masterpassword.h" - #include "utils.h" - - #define DIALOG_WIDTH 420 -@@ -156,6 +157,57 @@ gchar *input_dialog_query_password(const gchar *server, const gchar *user) - return pass; - } - -+gchar *input_dialog_query_master_password(void) -+{ -+ gchar *mp_input; -+ -+ mp_input = input_dialog_with_invisible(_("Input password"), -+ _("Master password"), -+ NULL); -+ -+ if (prefs_common.use_master_password && -+ prefs_common.auto_unload_master_password) -+ { -+ g_timeout_add(1000 * 30, unload_master_password, NULL); -+ debug_print("Master password to be unloaded in 30 seconds\n"); -+ } -+ -+ return mp_input; -+} -+ -+gchar *input_dialog_set_new_password(guint max_attempts) -+{ -+ guint cnt; -+ gchar *pass1, *pass2; -+ -+ if (max_attempts < 1) -+ return NULL; -+ -+ for (cnt = 0; cnt < max_attempts; ++cnt) { -+ pass1 = input_dialog_with_invisible( -+ _("Input password"), -+ _("New password"), -+ NULL); -+ pass2 = input_dialog_with_invisible( -+ _("Input password"), -+ _("Confirm new password"), -+ NULL); -+ -+ if (pass1 != NULL && pass2 != NULL && strcmp(pass1, pass2) == 0) { -+ cleanse_buffer(pass2, strlen(pass2)); -+ g_free(pass2); -+ break; -+ } -+ cleanse_buffer(pass1, strlen(pass1)); -+ cleanse_buffer(pass2, strlen(pass2)); -+ g_free(pass2); -+ g_free(pass1); -+ pass1 = NULL; -+ } -+ -+ return pass1; -+} -+ - gchar *input_dialog_with_filesel(const gchar *title, const gchar *message, - const gchar *default_string, - GtkFileChooserAction action) -diff --git a/src/inputdialog.h b/src/inputdialog.h -index 02bdda3..5364d1a 100644 ---- a/src/inputdialog.h -+++ b/src/inputdialog.h -@@ -36,7 +36,8 @@ gchar *input_dialog_combo (const gchar *title, - gboolean case_sensitive); - gchar *input_dialog_query_password (const gchar *server, - const gchar *user); -- -+gchar *input_dialog_query_master_password(void); -+gchar *input_dialog_set_new_password(guint max_attempts); - gchar *input_dialog_with_filesel (const gchar *title, - const gchar *message, - const gchar *default_string, -diff --git a/src/main.c b/src/main.c -index 77d8192..97a4bd0 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -87,6 +87,7 @@ - #include "foldersel.h" - #include "update_check.h" - #include "colorlabel.h" -+#include "masterpassword.h" - - #if USE_GPGME - # include "rfc2015.h" -@@ -265,14 +266,54 @@ int main(int argc, char *argv[]) - set_ui_update_func(gtkut_events_flush); - set_progress_func(main_window_progress_show); - set_input_query_password_func(input_dialog_query_password); -+ set_input_set_new_password_func(input_dialog_set_new_password); - #if USE_SSL - ssl_init(); - ssl_set_verify_func(ssl_manager_verify_cert); -+ set_input_query_master_password_func(input_dialog_query_master_password); - #endif - - CHDIR_EXIT_IF_FAIL(get_home_dir(), 1); - - prefs_common_read_config(); -+ set_master_password(NULL); -+#if USE_SSL -+ if (prefs_common.use_master_password) { -+ if (prefs_common.master_password_hash != NULL) { -+ if (check_master_password_interactively(3) != MP_RC_OK) { -+ if (alertpanel(_("Master password"), -+ _("Invalid master password"), -+ GTK_STOCK_DISCARD, -+ GTK_STOCK_QUIT, -+ NULL) != G_ALERTDEFAULT) { -+ exit(1); -+ } -+ } -+ } else { -+ alertpanel_notice( -+ _("Master password enabled but not set. Setting one now")); -+ if (set_master_password_interactively(3) != MP_RC_OK) { -+ if (alertpanel(_("Master password"), -+ _("Unable to set master password"), -+ GTK_STOCK_DISCARD, -+ GTK_STOCK_QUIT, -+ NULL) != G_ALERTDEFAULT) { -+ exit(1); -+ } -+ } -+ } -+ } -+ /* security goal: -+ * if the master password is enabled and loaded on init, -+ * an attacker can potentially set use_master_password - disabled -+ * afterwards and then force Sylpheed to save account data - the result -+ * being passwords saved to accountrc in plain-text. -+ * possible solution: -+ * Do not allow the passwords to be stored in plain-text if Sylpheed -+ * was started with use_master_password - enabled. -+ */ -+ master_password_enabled_on_init = prefs_common.use_master_password; -+#endif - filter_set_addressbook_func(addressbook_has_address); - filter_read_config(); - prefs_actions_read_config(); -@@ -381,6 +422,13 @@ int main(int argc, char *argv[]) - - remote_command_exec(); - -+#if USE_SSL -+ if (prefs_common.auto_unload_master_password && master_password_active()) { -+ debug_print("Auto unloading master password\n"); -+ unload_master_password(); -+ } -+#endif -+ - #if USE_UPDATE_CHECK - if (prefs_common.auto_update_check) - update_check(FALSE); -@@ -993,6 +1041,7 @@ void app_will_exit(gboolean force) - - /* remove temporary files, close log file, socket cleanup */ - #if USE_SSL -+ unload_master_password(); - ssl_done(); - #endif - syl_cleanup(); -diff --git a/src/prefs_account_dialog.c b/src/prefs_account_dialog.c -index e9cba13..e215b8e 100644 ---- a/src/prefs_account_dialog.c -+++ b/src/prefs_account_dialog.c -@@ -267,7 +267,7 @@ static PrefsUIData ui_data[] = { - {"user_id", &basic.uid_entry, - prefs_set_data_from_entry, prefs_set_entry}, - {"password", &basic.pass_entry, -- prefs_set_data_from_entry, prefs_set_entry}, -+ prefs_set_data_from_epass_entry, prefs_set_entry}, - - /* Receive */ - {"use_apop_auth", &receive.use_apop_chkbtn, -@@ -313,7 +313,7 @@ static PrefsUIData ui_data[] = { - {"smtp_user_id", &p_send.smtp_uid_entry, - prefs_set_data_from_entry, prefs_set_entry}, - {"smtp_password", &p_send.smtp_pass_entry, -- prefs_set_data_from_entry, prefs_set_entry}, -+ prefs_set_data_from_epass_entry, prefs_set_entry}, - - {"pop_before_smtp", &p_send.pop_bfr_smtp_chkbtn, - prefs_set_data_from_toggle, prefs_set_toggle}, -diff --git a/src/prefs_common_dialog.c b/src/prefs_common_dialog.c -index b46c9b7..119a056 100644 ---- a/src/prefs_common_dialog.c -+++ b/src/prefs_common_dialog.c -@@ -205,6 +205,7 @@ static struct JunkMail { - static struct Privacy { - GtkWidget *checkbtn_auto_check_signatures; - GtkWidget *checkbtn_gpg_signature_popup; -+ GtkWidget *radiobtn_gpg_signature_all; - GtkWidget *checkbtn_store_passphrase; - GtkWidget *spinbtn_store_passphrase; - GtkObject *spinbtn_store_passphrase_adj; -@@ -215,6 +216,14 @@ static struct Privacy { - } privacy; - #endif - -+#if USE_SSL -+static struct MasterPassword { -+ GtkWidget *checkbtn_use_master_password; -+ GtkWidget *checkbtn_auto_unload_master_password; -+ GtkWidget *spinbtn_encrypted_password_min_length; -+} master_password; -+#endif -+ - static struct Interface { - GtkWidget *checkbtn_always_show_msg; - GtkWidget *checkbtn_always_mark_read; -@@ -314,6 +323,13 @@ static void prefs_common_attach_toolbtn_pos_set_radiobtn (PrefParam *pparam); - static void prefs_common_online_mode_set_data_from_radiobtn(PrefParam *pparam); - static void prefs_common_online_mode_set_radiobtn (PrefParam *pparam); - -+#if USE_GPGME -+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn( -+ PrefParam *pparam); -+static void prefs_common_gpg_signature_popup_mode_set_radiobtn( -+ PrefParam *pparam); -+#endif -+ - static PrefsUIData ui_data[] = { - /* Receive */ - {"autochk_newmail", &receive.checkbtn_autochk, -@@ -540,6 +556,9 @@ static PrefsUIData ui_data[] = { - prefs_set_data_from_toggle, prefs_set_toggle}, - {"gpg_signature_popup", &privacy.checkbtn_gpg_signature_popup, - prefs_set_data_from_toggle, prefs_set_toggle}, -+ {"gpg_signature_popup_mode", &privacy.radiobtn_gpg_signature_all, -+ prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn, -+ prefs_common_gpg_signature_popup_mode_set_radiobtn}, - {"store_passphrase", &privacy.checkbtn_store_passphrase, - prefs_set_data_from_toggle, prefs_set_toggle}, - {"store_passphrase_timeout", &privacy.spinbtn_store_passphrase, -@@ -552,6 +571,18 @@ static PrefsUIData ui_data[] = { - prefs_set_data_from_toggle, prefs_set_toggle}, - #endif /* USE_GPGME */ - -+#if USE_SSL -+ {"use_master_password", &master_password.checkbtn_use_master_password, -+ prefs_set_data_from_toggle, prefs_set_toggle}, -+ {"auto_unload_master_password", -+ &master_password.checkbtn_auto_unload_master_password, -+ prefs_set_data_from_toggle, prefs_set_toggle}, -+ {"encrypted_password_min_length", -+ &master_password.spinbtn_encrypted_password_min_length, -+ prefs_set_data_from_spinbtn, -+ prefs_set_spinbtn}, -+#endif -+ - /* Interface */ - {"always_show_message_when_selected", - &iface.checkbtn_always_show_msg, -@@ -682,6 +713,9 @@ static void prefs_junk_create (void); - #if USE_GPGME - static void prefs_privacy_create (void); - #endif -+#if USE_SSL -+static void prefs_master_password_create (void); -+#endif - static void prefs_details_create (void); - static GtkWidget *prefs_other_create (void); - static GtkWidget *prefs_extcmd_create (void); -@@ -841,6 +875,10 @@ static void prefs_common_create(void) - #if USE_GPGME - prefs_privacy_create(); - SET_NOTEBOOK_LABEL(dialog.notebook, _("Privacy"), page++); -+#endif -+#if USE_SSL -+ prefs_master_password_create(); -+ SET_NOTEBOOK_LABEL(dialog.notebook, _("Master password"), page++); - #endif - prefs_details_create(); - SET_NOTEBOOK_LABEL(dialog.notebook, _("Details"), page++); -@@ -2478,10 +2516,14 @@ static void prefs_privacy_create(void) - GtkWidget *vbox2; - GtkWidget *vbox3; - GtkWidget *hbox1; -+ GtkWidget *vbox_sign_popup_mode; -+ GtkWidget *hbox_sign_popup_mode; - GtkWidget *hbox_spc; - GtkWidget *label; - GtkWidget *checkbtn_auto_check_signatures; - GtkWidget *checkbtn_gpg_signature_popup; -+ GtkWidget *radiobtn_gpg_signature_all; -+ GtkWidget *radiobtn_gpg_signature_bad; - GtkWidget *checkbtn_store_passphrase; - GtkObject *spinbtn_store_passphrase_adj; - GtkWidget *spinbtn_store_passphrase; -@@ -2505,6 +2547,56 @@ static void prefs_privacy_create(void) - PACK_CHECK_BUTTON (vbox2, checkbtn_gpg_signature_popup, - _("Show signature check result in a popup window")); - -+ vbox_sign_popup_mode = gtk_vbox_new (FALSE, VSPACING_NARROW); -+ gtk_widget_show (vbox_sign_popup_mode); -+ gtk_box_pack_start (GTK_BOX (vbox2), vbox_sign_popup_mode, FALSE, FALSE, 0); -+ -+ hbox_sign_popup_mode = gtk_hbox_new (FALSE, 8); -+ gtk_widget_show (hbox_sign_popup_mode); -+ gtk_box_pack_start (GTK_BOX (vbox_sign_popup_mode), -+ hbox_sign_popup_mode, -+ FALSE, -+ FALSE, -+ 0); -+ -+ hbox_spc = gtk_hbox_new (FALSE, 0); -+ gtk_widget_show (hbox_spc); -+ gtk_box_pack_start (GTK_BOX (hbox_sign_popup_mode), -+ hbox_spc, -+ FALSE, -+ FALSE, -+ 0); -+ gtk_widget_set_size_request (hbox_spc, 12, -1); -+ -+ radiobtn_gpg_signature_all = gtk_radio_button_new_with_label( -+ NULL, -+ _("All signatures")); -+ gtk_widget_show(radiobtn_gpg_signature_all); -+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode), -+ radiobtn_gpg_signature_all, -+ FALSE, -+ FALSE, -+ 0); -+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_all), -+ MENU_VAL_ID, -+ GINT_TO_POINTER (1)); -+ -+ radiobtn_gpg_signature_bad = gtk_radio_button_new_with_label_from_widget( -+ GTK_RADIO_BUTTON (radiobtn_gpg_signature_all), -+ _("Bad signatures only")); -+ gtk_widget_show(radiobtn_gpg_signature_bad); -+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode), -+ radiobtn_gpg_signature_bad, -+ FALSE, -+ FALSE, -+ 0); -+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_bad), -+ MENU_VAL_ID, -+ GINT_TO_POINTER (0)); -+ -+ SET_TOGGLE_SENSITIVITY (checkbtn_gpg_signature_popup, -+ hbox_sign_popup_mode); -+ - PACK_CHECK_BUTTON (vbox2, checkbtn_store_passphrase, - _("Store passphrase in memory temporarily")); - -@@ -2572,7 +2664,8 @@ static void prefs_privacy_create(void) - = checkbtn_auto_check_signatures; - privacy.checkbtn_gpg_signature_popup - = checkbtn_gpg_signature_popup; -- privacy.checkbtn_store_passphrase = checkbtn_store_passphrase; -+ privacy.radiobtn_gpg_signature_all = radiobtn_gpg_signature_all; -+ privacy.checkbtn_store_passphrase = checkbtn_store_passphrase; - privacy.spinbtn_store_passphrase = spinbtn_store_passphrase; - privacy.spinbtn_store_passphrase_adj = spinbtn_store_passphrase_adj; - #ifndef G_OS_WIN32 -@@ -2582,6 +2675,93 @@ static void prefs_privacy_create(void) - } - #endif /* USE_GPGME */ - -+#if USE_SSL -+static void prefs_master_password_create(void) -+{ -+ GtkWidget *vbox_main; -+ GtkWidget *vbox_master_password_options; -+ GtkWidget *vbox_master_password_suboptions; -+ GtkWidget *hbox1; -+ GtkWidget *hbox_spc; -+ GtkWidget *label; -+ GtkWidget *checkbtn_use_master_password; -+ GtkWidget *checkbtn_auto_unload_master_password; -+ GtkWidget *spinbtn_encrypted_password_min_length; -+ GtkObject *spinbtn_encrypted_password_min_length_adj; -+ -+ vbox_main = gtk_vbox_new (FALSE, VSPACING); -+ gtk_widget_show (vbox_main); -+ gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox_main); -+ gtk_container_set_border_width (GTK_CONTAINER (vbox_main), VBOX_BORDER); -+ -+ vbox_master_password_options = gtk_vbox_new (FALSE, 0); -+ gtk_widget_show (vbox_master_password_options); -+ gtk_box_pack_start ( -+ GTK_BOX (vbox_main), vbox_master_password_options, FALSE, FALSE, 0); -+ -+ PACK_CHECK_BUTTON (vbox_master_password_options, -+ checkbtn_use_master_password, -+ _("Use master password")); -+ -+ vbox_master_password_suboptions = gtk_vbox_new (FALSE, VSPACING_NARROW); -+ gtk_widget_show (vbox_master_password_suboptions); -+ gtk_box_pack_start (GTK_BOX (vbox_master_password_options), -+ vbox_master_password_suboptions, -+ FALSE, -+ FALSE, -+ 0); -+ -+ PACK_CHECK_BUTTON (vbox_master_password_suboptions, -+ checkbtn_auto_unload_master_password, -+ _("Automatically unload master password " -+ "after session initialization")); -+ -+ hbox1 = gtk_hbox_new (FALSE, 8); -+ gtk_widget_show (hbox1); -+ gtk_box_pack_start (GTK_BOX (vbox_master_password_suboptions), -+ hbox1, -+ FALSE, -+ FALSE, -+ 0); -+ -+ hbox_spc = gtk_hbox_new (FALSE, 0); -+ gtk_widget_show (hbox_spc); -+ gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0); -+ gtk_widget_set_size_request (hbox_spc, 12, -1); -+ -+ label = gtk_label_new (_("Min. password length")); -+ gtk_widget_show (label); -+ gtk_box_pack_start ( -+ GTK_BOX (hbox1), label, FALSE, FALSE, 0); -+ -+ spinbtn_encrypted_password_min_length_adj = gtk_adjustment_new ( -+ 32, 0, 99, 1, 5, 0); -+ spinbtn_encrypted_password_min_length = gtk_spin_button_new( -+ GTK_ADJUSTMENT (spinbtn_encrypted_password_min_length_adj), 1, 0); -+ gtk_widget_show (spinbtn_encrypted_password_min_length); -+ gtk_box_pack_start (GTK_BOX (hbox1), -+ spinbtn_encrypted_password_min_length, -+ FALSE, -+ FALSE, -+ 0); -+ gtk_spin_button_set_numeric ( -+ GTK_SPIN_BUTTON (spinbtn_encrypted_password_min_length), TRUE); -+ gtk_widget_set_size_request (spinbtn_encrypted_password_min_length, -+ 64, -+ -1); -+ -+ SET_TOGGLE_SENSITIVITY (checkbtn_use_master_password, -+ vbox_master_password_suboptions); -+ -+ master_password.checkbtn_use_master_password -+ = checkbtn_use_master_password; -+ master_password.checkbtn_auto_unload_master_password -+ = checkbtn_auto_unload_master_password; -+ master_password.spinbtn_encrypted_password_min_length -+ = spinbtn_encrypted_password_min_length; -+} -+#endif /* USE_SSL */ -+ - static void prefs_details_create(void) - { - GtkWidget *vbox1; -@@ -4719,6 +4899,60 @@ static void prefs_common_online_mode_set_radiobtn(PrefParam *pparam) - } - } - -+#if USE_GPGME -+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn( -+ PrefParam *pparam) -+{ -+ PrefsUIData *ui_data; -+ GtkRadioButton *radiobtn; -+ GSList *group; -+ -+ ui_data = (PrefsUIData *)pparam->ui_data; -+ g_return_if_fail(ui_data != NULL); -+ g_return_if_fail(*ui_data->widget != NULL); -+ -+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); -+ group = gtk_radio_button_get_group(radiobtn); -+ while (group != NULL) { -+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); -+ -+ if (gtk_toggle_button_get_active(btn)) { -+ prefs_common.gpg_signature_popup_mode = -+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), MENU_VAL_ID)); -+ break; -+ } -+ group = group->next; -+ } -+} -+ -+static void prefs_common_gpg_signature_popup_mode_set_radiobtn( -+ PrefParam *pparam) -+{ -+ PrefsUIData *ui_data; -+ GtkRadioButton *radiobtn; -+ GSList *group; -+ -+ ui_data = (PrefsUIData *)pparam->ui_data; -+ g_return_if_fail(ui_data != NULL); -+ g_return_if_fail(*ui_data->widget != NULL); -+ -+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); -+ group = gtk_radio_button_get_group(radiobtn); -+ while (group != NULL) { -+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); -+ gint data; -+ -+ data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), -+ MENU_VAL_ID)); -+ if (data == prefs_common.gpg_signature_popup_mode) { -+ gtk_toggle_button_set_active(btn, TRUE); -+ break; -+ } -+ group = group->next; -+ } -+} -+#endif -+ - static void prefs_common_dispitem_clicked(void) - { - prefs_summary_column_open(FOLDER_ITEM_IS_SENT_FOLDER -diff --git a/src/prefs_ui.c b/src/prefs_ui.c -index a3a8f74..2ab1d45 100644 ---- a/src/prefs_ui.c -+++ b/src/prefs_ui.c -@@ -31,11 +31,13 @@ - #include - - #include "prefs.h" -+#include "prefs_common.h" - #include "prefs_ui.h" - #include "menu.h" - #include "codeconv.h" - #include "utils.h" - #include "gtkutils.h" -+#include "masterpassword.h" - - typedef enum - { -@@ -268,6 +270,65 @@ void prefs_set_data_from_entry(PrefParam *pparam) - } - } - -+void prefs_set_data_from_epass_entry(PrefParam *pparam) -+{ -+#if USE_SSL -+ PrefsUIData *ui_data; -+ gchar **str; -+ const gchar *entry_str; -+ -+ /* This is where decrypted passwords are encrypted and stored again */ -+ -+ /* master_password == NULL for any of the following reasons: -+ * - use_master_password is not enabled (we just save without encrypting) -+ * - master_password is auto unloaded (prompt for the master password) -+ * - undefined reason (refure to store the password) -+ */ -+ if (!prefs_common.use_master_password) { -+ /* check if use_master_password was enabled when Sylpheed started */ -+ if (!master_password_enabled_on_init) { -+ prefs_set_data_from_entry(pparam); -+ } -+ return; -+ } else if (master_password == NULL) { -+ if (!prefs_common.auto_unload_master_password) { -+ debug_print("Master password enabled, but not loaded for no " -+ "apparent reason. Not storing\n"); -+ return; -+ } else { -+ if (check_master_password_interactively(3) != MP_RC_OK) { -+ debug_print("Failed to reload the master password\n"); -+ return; -+ } -+ } -+ } -+ -+ ui_data = (PrefsUIData *)pparam->ui_data; -+ g_return_if_fail(ui_data != NULL); -+ g_return_if_fail(*ui_data->widget != NULL); -+ -+ entry_str = gtk_entry_get_text(GTK_ENTRY(*ui_data->widget)); -+ -+ switch (pparam->type) { -+ case P_STRING: -+ str = (gchar **)pparam->data; -+ g_free(*str); -+ if ((entry_str != NULL) && (!mpes_string_prefix(entry_str))) { -+ debug_print("Encrypting GTK password entry\n"); -+ *str = encrypt_with_master_password(entry_str); -+ } else { -+ *str = entry_str[0] ? g_strdup(entry_str) : NULL; -+ } -+ break; -+ default: -+ g_warning("Invalid PrefType for GtkEntry widget: %d\n", -+ pparam->type); -+ } -+#else -+ prefs_set_data_from_entry(pparam); -+#endif -+} -+ - void prefs_set_entry(PrefParam *pparam) - { - PrefsUIData *ui_data; -diff --git a/src/prefs_ui.h b/src/prefs_ui.h -index cfdf353..f7bd903 100644 ---- a/src/prefs_ui.h -+++ b/src/prefs_ui.h -@@ -164,6 +164,7 @@ void prefs_set_data_from_dialog (PrefParam *param); - void prefs_set_dialog_to_default(PrefParam *param); - - void prefs_set_data_from_entry (PrefParam *pparam); -+void prefs_set_data_from_epass_entry(PrefParam *pparam); - void prefs_set_entry (PrefParam *pparam); - void prefs_set_data_from_text (PrefParam *pparam); - void prefs_set_text (PrefParam *pparam); -diff --git a/src/rfc2015.c b/src/rfc2015.c -index ebfa96c..48825e2 100644 ---- a/src/rfc2015.c -+++ b/src/rfc2015.c -@@ -210,8 +210,12 @@ static void check_signature(MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp) - gchar *tmp_file; - gint n_exclude_chars = 0; - -- if (prefs_common.gpg_signature_popup) -+ if (prefs_common.gpg_signature_popup && -+ prefs_common.gpg_signature_popup_mode == 1) -+ { -+ /* FIXME: Perhaps some macro instead of 1 */ - statuswindow = gpgmegtk_sig_status_create(); -+ } - - err = gpgme_new(&ctx); - if (err) { -@@ -309,8 +313,21 @@ leave: - result = _("Error verifying the signature"); - } - debug_print("verification status: %s\n", result); -- if (prefs_common.gpg_signature_popup) -+ if (prefs_common.gpg_signature_popup && -+ prefs_common.gpg_signature_popup_mode == 1) -+ { -+ /* FIXME: Perhaps some macro instead of 1 */ -+ gpgmegtk_sig_status_update(statuswindow, ctx); -+ } else if (prefs_common.gpg_signature_popup && -+ verifyresult->signatures->status != GPG_ERR_NO_DATA && -+ verifyresult->signatures->status != GPG_ERR_NO_ERROR) -+ { -+ /* prefs_common.gpg_signature_popup_mode == 0 is useless as long as -+ * there are only two modes (0 and 1) -+ */ -+ statuswindow = gpgmegtk_sig_status_create(); - gpgmegtk_sig_status_update(statuswindow, ctx); -+ } - - g_free (partinfo->sigstatus); - partinfo->sigstatus = g_strdup (result); -@@ -319,8 +336,11 @@ leave: - gpgme_data_release(text); - if (ctx) - gpgme_release(ctx); -- if (prefs_common.gpg_signature_popup) -+ if (prefs_common.gpg_signature_popup) { -+ /* gpgmegtk_sig_status_destroy handles NULL params so no complicated -+ check is needed */ - gpgmegtk_sig_status_destroy(statuswindow); -+ } - } - - /* -@@ -427,7 +447,11 @@ static gpgme_data_t pgp_decrypt(MsgInfo *msginfo, MimeInfo *partinfo, FILE *fp) - debug_print("verification status: %s\n", result); - debug_print("full status: %s\n", - msginfo->encinfo->sigstatus_full); -- if (prefs_common.gpg_signature_popup) { -+ if (prefs_common.gpg_signature_popup && -+ ((prefs_common.gpg_signature_popup_mode == 1) || -+ (verifyresult->signatures->status != GPG_ERR_NO_DATA && -+ verifyresult->signatures->status != GPG_ERR_NO_ERROR))) -+ { - GpgmegtkSigStatus statuswindow; - statuswindow = gpgmegtk_sig_status_create(); - gpgmegtk_sig_status_update(statuswindow, ctx); -diff --git a/src/send_message.c b/src/send_message.c -index a8c2c7a..537c3c8 100644 ---- a/src/send_message.c -+++ b/src/send_message.c -@@ -58,6 +58,7 @@ - #include "inc.h" - #include "mainwindow.h" - #include "summaryview.h" -+#include "masterpassword.h" - - #define SMTP_PORT 25 - #if USE_SSL -@@ -648,13 +649,13 @@ static gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp) - - if (ac_prefs->smtp_userid) { - smtp_session->user = g_strdup(ac_prefs->smtp_userid); -- if (ac_prefs->smtp_passwd) -- smtp_session->pass = -- g_strdup(ac_prefs->smtp_passwd); -- else if (ac_prefs->tmp_smtp_pass) -+ if (ac_prefs->smtp_passwd) { -+ smtp_session->pass = decrypt_with_master_password( -+ ac_prefs->smtp_passwd); -+ } else if (ac_prefs->tmp_smtp_pass) { - smtp_session->pass = - g_strdup(ac_prefs->tmp_smtp_pass); -- else { -+ } else { - smtp_session->pass = - input_query_password - (ac_prefs->smtp_server, -- cgit v1.3