summaryrefslogtreecommitdiff
path: root/mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch
diff options
context:
space:
mode:
authorSimeon Simeonov2024-06-30 19:06:22 +0200
committerSimeon Simeonov2024-06-30 19:06:22 +0200
commit991d0d96e5e079dae7feef4fc9c4736c306f6e57 (patch)
treee5ef1df00ea2b872f74a3c475bc17db400e37a30 /mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch
parent8459629cd213b551f3ccb6a519247594fa137425 (diff)
Fix legacy ebuilds and fix pkgcheck issues
Diffstat (limited to 'mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch')
-rw-r--r--mail-client/sylpheed/files/sylpheed-3.7.0-ALL.patch2149
1 files changed, 0 insertions, 2149 deletions
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 @@
1diff --git a/.gitignore b/.gitignore
2new file mode 100644
3index 0000000..9742351
4--- /dev/null
5+++ b/.gitignore
6@@ -0,0 +1,21 @@
7+*.o
8+*.lo
9+*.la
10+Makefile
11+config.h
12+config.log
13+config.status
14+libsylph/.deps/
15+libsylph/.libs/
16+libtool
17+plugin/attachment_tool/.deps/
18+plugin/attachment_tool/.libs/
19+plugin/test/.deps/
20+plugin/test/.libs/
21+po/Makefile.in
22+po/POTFILES
23+src/.deps/
24+src/.libs/
25+src/sylpheed
26+src/sylpheed.rc
27+stamp-h1
28diff --git a/README.md b/README.md
29new file mode 100644
30index 0000000..1cf6d21
31--- /dev/null
32+++ b/README.md
33@@ -0,0 +1,219 @@
34+## Implemented features and fixes not present in the official Sylpheed release
35+
36+- (fix)
37+ PGP signature not verified properly when the message has no newline
38+ at the end. https://sylpheed.sraoss.jp/redmine/issues/288
39+
40+- (feature)
41+ Make it possible to select "Show signature check result in a popup window"
42+ only for bad signatures.
43+
44+- (feature)
45+ Support for encrypting and storing encrypted passwords using a master password.
46+ Read bellow for more details!
47+
48+
49+## Master password
50+
51+The master password feature is developed by Simeon Simeonov (sgs)
52+and is currently in an experimental state.
53+
54+
55+### Motivation
56+
57+Currently Sylpheed is storing passwords in plain-text. One can always refrain
58+from storing passwords and let Sylpheed prompt for them, but the more accounts
59+one has, the more annoying this becomes.
60+
61+The goal is to have the passwords stored in a secure way and let Sylpheed only
62+prompt for the master password.
63+
64+
65+### Security goals
66+
67+- attacker (A) should not be able to derive the password from the digest.
68+
69+- A should not be able to derive the master password even if she has
70+ read and write access to the storage.
71+
72+- A should not be able to determine the length of the encrypted password
73+ even if she has read and write access to the storage.
74+
75+- A should not be able to craft an edited password without obtaining the
76+ master password.
77+
78+- A should not be able to compromise the master password or force Sylpheed
79+ to store decrypted passwords even if she has access to a running Sylpheed.
80+
81+- a warning / prompt should be given if a user accidentally types in a "wrong"
82+ master password before decryption is initiated.
83+
84+
85+### Usage in Sylpheed
86+
87+- backup your Sylpheed profile (often $HOME/.sylpheed-2.0)!
88+
89+- start Sylpheed and open "Configuration" -> "Common preferences..."!
90+
91+- select the "Master password" tab, enable "Use master password" and
92+ apply the changes!
93+
94+- restart Sylpheed (exit and then start Sylpheed again)!
95+
96+- you will be asked to type and verify a new master password.
97+
98+- set your new passwords from the "Configuration" -> "Edit accounts..."!
99+
100+- select "Automatically unload master password after session initialization"
101+ for increased security and decreased convenience.
102+
103+Note:
104+Sylpheed will automatically convert existing stored passwords, but it will not
105+touch your backups. You will have to remove all remnants of plain-text
106+passwords manually.
107+
108+
109+### Choice of cryptographic primitives
110+
111+The primary concern when selecting cryptographic primitives was portability.
112+The desire was to go for primitives that are both strong and available in all
113+supported production distributions of OpenSSL and LibreSSL.
114+
115+
116+#### Cipher
117+
118+When it comes to implementation, there are several advantages in using stream
119+cipher or a block cipher that behaves like a stream cipher when used in a
120+certain mode of operation. One is avoiding to deal with padding.
121+
122+AES-256 operating in CFB was selected for these reasons.
123+ChaCha20 should be considered as a replacement in the future.
124+
125+
126+#### Hash-function
127+
128+Hash-functions are used for:
129+- key derivation
130+- plain-text digest
131+
132+Those operations do not have to use the same hash-function.
133+(See the "Encryption & decryption scheme" section for more details!)
134+
135+Key-derivation:
136+Since AES-256 uses a 256 bits key, we need a hash-function with at least
137+the same digest size or bigger.
138+Since the digest (which is the key itself) is considered confidential and is
139+stored only in memory for only a limited amount of time, SHA-256 is considered
140+sufficiently strong for that purpose.
141+
142+Size + plain-text + padding digest:
143+Since the digest is created of both the plain-text and the plain-text size,
144+as well as being encrypted, SHA-256 is considered sufficiently strong.
145+SHA-512 may increase security at the price of adding additional 32 bytes
146+to the encrypted password digest.
147+
148+Stronger hash-functions like SHA-3 or BLAKE2b can be considered as a
149+replacement in the future.
150+
151+
152+#### Master password digest
153+
154+In order to be able to decide whether the user typed a "wrong" master password,
155+before attempting to decrypt, Sylpheed stores a digest of the master password
156+in 'master_password_hash' in sylpheedrc.
157+100000 iterations of PBKDF2_HMAC with SHA-512 and 16 bytes salt is used.
158+Note that this digest is useless as a key and even if a plain-text that
159+produces the same digest is found, it will most probably be useless as a
160+master-password.
161+
162+
163+### Encryption & decryption scheme
164+
165+
166+#### Encryption
167+
168+
169+Input:
170+
171+- plain-text password to be encrypted (P)
172+
173+- plain-text master-password used for key derivation (M)
174+
175+- integer minimum password length (0 < L < 100)
176+
177+
178+Output:
179+
180+- an encrypted password digest (base64) (B)
181+
182+
183+Operation:
184+
185+- generate 16 bytes of random data to be used as a salt (S)
186+
187+- derive the key (K): K = SHA_256(S + M)
188+
189+- produce a 2 byte string (N) indicating the length of P
190+
191+- generate random padding and set N:
192+ - if the length of P < L, produce L - P bytes of random data (R), N = "%02d"
193+ - if the length of P >= L, N = "-1"
194+
195+- produce a hash digest (H): H = SHA_256(N + P + R (if the length of P < L))
196+
197+- encrypt (E): E = AES_256_CFB_ENCRYPT(H + N + P + R (if the length of P < L), K)
198+
199+- B = mpes1:BASE64_ENCODE(S + E)
200+
201+example:
202+mpes1:vo7lsIpD7i6byBA6+vlUoF4OVDfEe+aYRRk4FRtfJ2gMY8M43Kj6WfdfgbViIOl83bI4XEc96okhPW5Mla813aAR1gbPjDg0xmCyIbWOiUv/dg==
203+
204+
205+#### Decryption
206+
207+
208+Input:
209+
210+- encrypted password digest (base64) (B)
211+
212+- plain-text master-password used for key derivation (M)
213+
214+
215+Output:
216+
217+- plain-text password (P)
218+
219+
220+Operation:
221+
222+- remove the prefix (mpes1:) and base64-decode the rest of the digest: B = BASE64_DECODE(B)
223+
224+- fetch the first 16 bytes for the salt: S = B[0 : 15]
225+
226+- derive the key (K): K = SHA_256(S + M)
227+
228+- decrypt the rest of B (D): D = AES_256_CFB_DECRYPT(B[16 :], K)
229+
230+- extract the first 16 bytes for the hash digest (H): H = D[0 : 15]
231+
232+- in order to detect data-inconsistency, assert H == SHA_256(D[16 :])
233+
234+- extract the next 2 bytes for the length of P (N): N = D[16 : 17]
235+
236+- fetch the plain-text:
237+ - if N == "-1" the password is the remaining bytes of D: P = D[18 :]
238+ - if N != "-1", extract the next N-bytes from D: P = D[18 : (18 + N)]
239+
240+
241+### Limitations
242+
243+- currently only the 'password' and 'smtp_password' keys in accountrc
244+ are encrypted.
245+ A mechanism that allows for any key and even folders to be encrypted
246+ should be considered in the future.
247+
248+- currently it is not possible to select alternative ciphers, hash-functions
249+ and modes of operation (without editing the source code).
250+
251+- currently it is not possible to change your master password without having to
252+ (re)set your passwords manually.
253diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am
254index 8f2b76b..40d26a9 100644
255--- a/libsylph/Makefile.am
256+++ b/libsylph/Makefile.am
257@@ -19,6 +19,7 @@ libsylph_0_la_SOURCES = \
258 folder.c \
259 html.c \
260 imap.c \
261+ masterpassword.c \
262 mbox.c \
263 md5.c \
264 md5_hmac.c \
265@@ -62,6 +63,7 @@ libsylph_0include_HEADERS = \
266 folder.h \
267 html.h \
268 imap.h \
269+ masterpassword.h \
270 mbox.h \
271 md5.h \
272 md5_hmac.h \
273diff --git a/libsylph/Makefile.in b/libsylph/Makefile.in
274index 6e3c999..ac8fefd 100644
275--- a/libsylph/Makefile.in
276+++ b/libsylph/Makefile.in
277@@ -129,8 +129,8 @@ libsylph_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
278 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
279 am_libsylph_0_la_OBJECTS = account.lo base64.lo codeconv.lo \
280 customheader.lo displayheader.lo filter.lo folder.lo html.lo \
281- imap.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo nntp.lo \
282- pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \
283+ imap.lo masterpassword.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo \
284+ nntp.lo pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \
285 procmime.lo procmsg.lo quoted-printable.lo recv.lo session.lo \
286 smtp.lo socket.lo socks.lo ssl.lo ssl_hostname_validation.lo \
287 stringtable.lo sylmain.lo unmime.lo utils.lo uuencode.lo \
288@@ -402,6 +402,7 @@ libsylph_0_la_SOURCES = \
289 folder.c \
290 html.c \
291 imap.c \
292+ masterpassword.c \
293 mbox.c \
294 md5.c \
295 md5_hmac.c \
296@@ -445,6 +446,7 @@ libsylph_0include_HEADERS = \
297 folder.h \
298 html.h \
299 imap.h \
300+ masterpassword.h \
301 mbox.h \
302 md5.h \
303 md5_hmac.h \
304@@ -579,6 +581,7 @@ distclean-compile:
305 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Plo@am__quote@
306 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Plo@am__quote@
307 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Plo@am__quote@
308+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterpassword.Plo@am__quote@
309 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox.Plo@am__quote@
310 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@
311 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_hmac.Plo@am__quote@
312diff --git a/libsylph/defs.h b/libsylph/defs.h
313index 9e3f82b..67325bf 100644
314--- a/libsylph/defs.h
315+++ b/libsylph/defs.h
316@@ -58,6 +58,9 @@
317 #define DISPLAY_HEADER_RC "dispheaderrc"
318 #define MENU_RC "menurc"
319 #define ACTIONS_RC "actionsrc"
320+#ifdef USE_SSL
321+#define SECURE_RC "securerc"
322+#endif
323 #define COMMAND_HISTORY "command_history"
324 #define TEMPLATE_DIR "templates"
325 #define TMP_DIR "tmp"
326diff --git a/libsylph/imap.c b/libsylph/imap.c
327index aa0d737..e1c7cfd 100644
328--- a/libsylph/imap.c
329+++ b/libsylph/imap.c
330@@ -52,6 +52,7 @@
331 #include "utils.h"
332 #include "prefs_common.h"
333 #include "virtual.h"
334+#include "masterpassword.h"
335
336 #define IMAP4_PORT 143
337 #if USE_SSL
338@@ -705,9 +706,13 @@ static gint imap_session_connect(IMAPSession *session)
339 account = (PrefsAccount *)(SESSION(session)->data);
340
341 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
342- SESSION(session)->server, SESSION(session)->port);
343-
344- pass = account->passwd;
345+ SESSION(session)->server, SESSION(session)->port);
346+ if (master_password_active()) {
347+ pass = decrypt_with_master_password(account->passwd);
348+ /* a new string is allocated. To be removed ... */
349+ } else {
350+ pass = account->passwd;
351+ }
352 if (!pass)
353 pass = account->tmp_pass;
354 if (!pass) {
355@@ -770,8 +775,11 @@ static gint imap_session_connect(IMAPSession *session)
356 #endif
357
358 if (!session->authenticated &&
359- imap_auth(session, account->userid, pass, account->imap_auth_type)
360- != IMAP_SUCCESS) {
361+ imap_auth(session, account->userid, pass, account->imap_auth_type)
362+ != IMAP_SUCCESS) {
363+ if (master_password_active()) {
364+ g_free(pass); /* remove the decrypted password */
365+ }
366 if (account->tmp_pass) {
367 g_free(account->tmp_pass);
368 account->tmp_pass = NULL;
369@@ -780,6 +788,10 @@ static gint imap_session_connect(IMAPSession *session)
370 return IMAP_AUTHFAIL;
371 }
372
373+ if (master_password_active()) {
374+ g_free(pass); /* remove the decrypted password */
375+ }
376+
377 return IMAP_SUCCESS;
378 }
379
380diff --git a/libsylph/masterpassword.c b/libsylph/masterpassword.c
381new file mode 100644
382index 0000000..3d7af94
383--- /dev/null
384+++ b/libsylph/masterpassword.c
385@@ -0,0 +1,216 @@
386+/*
387+ * LibSylph -- E-Mail client library
388+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto
389+ *
390+ * This library is free software; you can redistribute it and/or
391+ * modify it under the terms of the GNU Lesser General Public
392+ * License as published by the Free Software Foundation; either
393+ * version 2.1 of the License, or (at your option) any later version.
394+ *
395+ * This library is distributed in the hope that it will be useful,
396+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
397+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
398+ * Lesser General Public License for more details.
399+ *
400+ * You should have received a copy of the GNU Lesser General Public
401+ * License along with this library; if not, write to the Free Software
402+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
403+ */
404+
405+#ifdef HAVE_CONFIG_H
406+#include "config.h"
407+#endif
408+
409+#include <glib/gi18n.h>
410+
411+#include "prefs_common.h"
412+#include "ssl.h"
413+#include "utils.h"
414+#include "masterpassword.h"
415+
416+
417+gchar *master_password;
418+gboolean master_password_enabled_on_init;
419+
420+void set_master_password(const char *password) {
421+ master_password = password;
422+}
423+
424+gchar *get_master_password(void) {
425+ return master_password;
426+}
427+
428+void cleanse_buffer(void *buf, size_t len) {
429+#if USE_SSL
430+ OPENSSL_cleanse(buf, len);
431+#else
432+ memset(buf, 0, len); /* better than nothing */
433+#endif
434+}
435+
436+void unload_master_password(void) {
437+
438+ debug_print("Unloading master password\n");
439+ if (master_password == NULL) {
440+ /* not loaded / already unloaded */
441+ return;
442+ }
443+ cleanse_buffer(master_password, strlen(master_password));
444+ g_free(master_password);
445+ master_password = NULL;
446+ debug_print("Master password unloaded\n");
447+
448+}
449+
450+gint mpes_string_prefix(const gchar *str) {
451+
452+ /* this function will be expanded in time as the format changes */
453+ if (g_str_has_prefix(str, "mpes1:"))
454+ return 6;
455+ return 0;
456+
457+}
458+
459+gboolean master_password_active(void) {
460+
461+#if USE_SSL
462+ return ((master_password != NULL) &&
463+ prefs_common.use_master_password);
464+#else
465+ return FALSE;
466+#endif
467+
468+}
469+
470+gchar *decrypt_with_master_password(const gchar *str) {
471+
472+#if USE_SSL
473+ gchar *new_str;
474+ gint str_prefix;
475+
476+ if ((!str) || (!prefs_common.use_master_password))
477+ return g_strdup(str);
478+
479+ if (master_password == NULL) {
480+ /* we have empty or auto unloaded master password */
481+ if ((!prefs_common.auto_unload_master_password) ||
482+ (check_master_password_interactively(3) != MP_RC_OK)) {
483+ return g_strdup(str);
484+ }
485+ debug_print("Reloaded master password\n");
486+ }
487+
488+ str_prefix = mpes_string_prefix(str);
489+ if (!str_prefix)
490+ return g_strdup(str);
491+
492+ if (decrypt_data(&new_str,
493+ str + str_prefix,
494+ master_password,
495+ strlen(str) + 1 - str_prefix) != MP_RC_OK) {
496+ OPENSSL_cleanse(new_str, strlen(new_str));
497+ g_free(new_str);
498+ return g_strdup(str);
499+ }
500+
501+ return new_str;
502+#else
503+ return g_strdup(str);
504+#endif
505+
506+}
507+
508+gchar *encrypt_with_master_password(const gchar *str) {
509+
510+#if USE_SSL
511+ gchar *new_str, *mpes1_str;
512+ gint length_encrypted;
513+
514+ if ((!str) || (!master_password_active()))
515+ return NULL;
516+
517+ /*
518+ * unlike the decrypt function, here it is up to the caller
519+ * to make sure that auto unloaded master password is handled properly
520+ */
521+
522+ if (encrypt_data(&new_str,
523+ &length_encrypted,
524+ str,
525+ master_password,
526+ strlen(str) + 1,
527+ prefs_common.encrypted_password_min_length,
528+ TRUE) != MP_RC_OK) {
529+ g_free(new_str);
530+ return NULL;
531+ }
532+
533+ mpes1_str = g_strdup_printf("mpes1:%s", new_str);
534+ g_free(new_str);
535+
536+ return mpes1_str;
537+#else
538+ return NULL;
539+#endif
540+
541+}
542+
543+#if USE_SSL
544+gint set_master_password_interactively(guint max_attempts) {
545+
546+ if (master_password == NULL)
547+ master_password = input_set_new_password(max_attempts);
548+
549+ if (master_password == NULL)
550+ return 1;
551+
552+ if (generate_password_hash(
553+ &prefs_common.master_password_hash,
554+ master_password,
555+ NULL) != MP_RC_OK) {
556+ /* should not really happen unless buggy code / library */
557+ g_free(prefs_common.master_password_hash);
558+ prefs_common.master_password_hash = NULL;
559+ debug_print(_("Could not generate master password hash"));
560+ return 1;
561+ }
562+
563+ prefs_common_write_config();
564+ return 0;
565+
566+}
567+
568+gint check_master_password_interactively(guint max_attempts) {
569+
570+ guint cnt;
571+
572+ g_return_val_if_fail(max_attempts > 0, 1);
573+ g_return_val_if_fail(prefs_common.master_password_hash != NULL, 1);
574+
575+ if (master_password != NULL) {
576+ /* password already cached */
577+ debug_print("Master password already cached\n");
578+ return check_password(master_password,
579+ prefs_common.master_password_hash);
580+ }
581+
582+ for (cnt = 0; cnt < max_attempts; ++cnt) {
583+ master_password = input_query_master_password();
584+ if (master_password == NULL) {
585+ /* input canceled or query_master_password_func not set */
586+ continue;
587+ }
588+ if (check_password(master_password,
589+ prefs_common.master_password_hash) == MP_RC_OK) {
590+ return MP_RC_OK; /* match */
591+ }
592+ debug_print(_("Wrong master password entered (%d)\n"), cnt);
593+ OPENSSL_cleanse(master_password, strlen(master_password));
594+ g_free(master_password);
595+ master_password = NULL;
596+ }
597+
598+ return 1; /* no match */
599+
600+}
601+#endif /* USE_SSL */
602diff --git a/libsylph/masterpassword.h b/libsylph/masterpassword.h
603new file mode 100644
604index 0000000..7254643
605--- /dev/null
606+++ b/libsylph/masterpassword.h
607@@ -0,0 +1,47 @@
608+/*
609+ * LibSylph -- E-Mail client library
610+ * Copyright (C) 1999-2018 Hiroyuki Yamamoto
611+ *
612+ * This library is free software; you can redistribute it and/or
613+ * modify it under the terms of the GNU Lesser General Public
614+ * License as published by the Free Software Foundation; either
615+ * version 2.1 of the License, or (at your option) any later version.
616+ *
617+ * This library is distributed in the hope that it will be useful,
618+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
619+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
620+ * Lesser General Public License for more details.
621+ *
622+ * You should have received a copy of the GNU Lesser General Public
623+ * License along with this library; if not, write to the Free Software
624+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
625+ */
626+
627+#ifndef __MASTERPASSWORD_H__
628+#define __MASTERPASSWORD_H__
629+
630+#ifdef HAVE_CONFIG_H
631+#include "config.h"
632+#endif
633+
634+#define MP_RC_OK 0
635+#define MP_RC_WRONG_HASH_OR_KEY 1
636+#define MP_RC_INVALID_FORMAT 2 /* invalid digest format */
637+
638+extern gchar *master_password;
639+extern gboolean master_password_enabled_on_init; /* m.p. enabled on init? */
640+void set_master_password(const char *password);
641+gchar *get_master_password(void);
642+void cleanse_buffer(void *buf, size_t len);
643+void unload_master_password(void);
644+gint mpes_string_prefix(const gchar *str);
645+gboolean master_password_active(void);
646+gchar *decrypt_with_master_password(const gchar *str);
647+gchar *encrypt_with_master_password(const gchar *str);
648+
649+#if USE_SSL
650+gint set_master_password_interactively(guint max_attempts);
651+gint check_master_password_interactively(guint max_attempts);
652+#endif /* USE_SSL */
653+
654+#endif /* __MASTERPASSWORD_H__ */
655diff --git a/libsylph/news.c b/libsylph/news.c
656index ffff9f9..36d3b10 100644
657--- a/libsylph/news.c
658+++ b/libsylph/news.c
659@@ -44,6 +44,7 @@
660 #include "utils.h"
661 #include "prefs_common.h"
662 #include "prefs_account.h"
663+#include "masterpassword.h"
664 #if USE_SSL
665 # include "ssl.h"
666 #endif
667@@ -249,10 +250,11 @@ static Session *news_session_new_for_folder(Folder *folder)
668 ac = folder->account;
669 if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
670 userid = ac->userid;
671- if (ac->passwd && ac->passwd[0])
672- passwd = g_strdup(ac->passwd);
673- else
674+ if (ac->passwd && ac->passwd[0]) {
675+ passwd = decrypt_with_master_password(ac->passwd);
676+ } else {
677 passwd = input_query_password(ac->nntp_server, userid);
678+ }
679 }
680
681 if (ac->use_socks && ac->use_socks_for_recv && ac->proxy_host) {
682diff --git a/libsylph/pop.c b/libsylph/pop.c
683index 8cb7f5c..85f3ed9 100644
684--- a/libsylph/pop.c
685+++ b/libsylph/pop.c
686@@ -39,6 +39,7 @@
687 #include "prefs_account.h"
688 #include "utils.h"
689 #include "recv.h"
690+#include "masterpassword.h"
691
692 gint pop3_greeting_recv (Pop3Session *session,
693 const gchar *msg);
694@@ -437,8 +438,9 @@ Session *pop3_session_new(PrefsAccount *account)
695 session->error_msg = NULL;
696
697 session->user = g_strdup(account->userid);
698- session->pass = account->passwd ? g_strdup(account->passwd) :
699- account->tmp_pass ? g_strdup(account->tmp_pass) : NULL;
700+ session->pass = account->passwd ? decrypt_with_master_password(
701+ account->passwd) : account->tmp_pass ? g_strdup(
702+ account->tmp_pass) : NULL;
703
704 SESSION(session)->server = g_strdup(account->recv_server);
705
706diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c
707index 1aecba9..54cbc89 100644
708--- a/libsylph/prefs_account.c
709+++ b/libsylph/prefs_account.c
710@@ -34,6 +34,7 @@
711 #include "customheader.h"
712 #include "account.h"
713 #include "utils.h"
714+#include "masterpassword.h"
715
716 static PrefsAccount tmp_ac_prefs;
717
718@@ -205,7 +206,7 @@ PrefsAccount *prefs_account_new(void)
719 void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
720 {
721 const gchar *p = label;
722- gchar *rcpath;
723+ gchar *rcpath, *tmp_str;
724 gint id;
725
726 g_return_if_fail(ac_prefs != NULL);
727@@ -229,6 +230,34 @@ void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
728 ac_prefs->use_apop_auth = TRUE;
729 }
730
731+ if (master_password_active()) {
732+ if ((ac_prefs->passwd != NULL) &&
733+ !mpes_string_prefix(ac_prefs->passwd)) {
734+ /* TODO: Perhaps some prompt? */
735+ debug_print(
736+ "%s -> converting passwd cleartext to encrypted\n",
737+ label);
738+ tmp_str = ac_prefs->passwd;
739+ ac_prefs->passwd = encrypt_with_master_password(ac_prefs->passwd);
740+ cleanse_buffer(tmp_str, strlen(tmp_str));
741+ g_free(tmp_str);
742+ }
743+
744+ if ((ac_prefs->smtp_passwd != NULL) &&
745+ !mpes_string_prefix(ac_prefs->smtp_passwd)) {
746+ /* TODO: Perhaps some prompt? */
747+ debug_print(
748+ "%s -> converting smtp_passwd from cleartext to encrypted\n",
749+ label);
750+ tmp_str = ac_prefs->smtp_passwd;
751+ ac_prefs->smtp_passwd = encrypt_with_master_password(
752+ ac_prefs->smtp_passwd);
753+ cleanse_buffer(tmp_str, strlen(tmp_str));
754+ g_free(tmp_str);
755+ }
756+
757+ }
758+
759 custom_header_read_config(ac_prefs);
760 }
761
762diff --git a/libsylph/prefs_common.c b/libsylph/prefs_common.c
763index 8ed74a3..192964e 100644
764--- a/libsylph/prefs_common.c
765+++ b/libsylph/prefs_common.c
766@@ -406,6 +406,8 @@ static PrefParam param[] = {
767 P_BOOL},
768 {"gpg_signature_popup", "FALSE", &prefs_common.gpg_signature_popup,
769 P_BOOL},
770+ {"gpg_signature_popup_mode", "1", &prefs_common.gpg_signature_popup_mode,
771+ P_INT},
772 {"store_passphrase", "FALSE", &prefs_common.store_passphrase, P_BOOL},
773 {"store_passphrase_timeout", "0",
774 &prefs_common.store_passphrase_timeout, P_INT},
775@@ -416,6 +418,17 @@ static PrefParam param[] = {
776 {"show_gpg_warning", "TRUE", &prefs_common.gpg_warning, P_BOOL},
777 #endif
778
779+ /* Master password */
780+ {"use_master_password", "FALSE", &prefs_common.use_master_password,
781+ P_BOOL},
782+ {"master_password_hash", NULL, &prefs_common.master_password_hash,
783+ P_STRING},
784+ {"encrypted_password_min_length", "32",
785+ &prefs_common.encrypted_password_min_length, P_INT},
786+ {"auto_unload_master_password", "FALSE",
787+ &prefs_common.auto_unload_master_password,
788+ P_BOOL},
789+
790 /* Interface */
791 {"separate_folder", "FALSE", &prefs_common.sep_folder, P_BOOL},
792 {"separate_message", "FALSE", &prefs_common.sep_msg, P_BOOL},
793diff --git a/libsylph/prefs_common.h b/libsylph/prefs_common.h
794index ba9ddb5..6f9c364 100644
795--- a/libsylph/prefs_common.h
796+++ b/libsylph/prefs_common.h
797@@ -243,11 +243,18 @@ struct _PrefsCommon
798 /* Privacy */
799 gboolean auto_check_signatures;
800 gboolean gpg_signature_popup;
801+ gint gpg_signature_popup_mode;
802 gboolean store_passphrase;
803 gint store_passphrase_timeout;
804 gboolean passphrase_grab;
805 gboolean gpg_warning;
806
807+ /* Master password */
808+ gboolean use_master_password;
809+ gchar *master_password_hash;
810+ guint encrypted_password_min_length;
811+ gboolean auto_unload_master_password;
812+
813 /* Interface */
814 gboolean sep_folder;
815 gboolean sep_msg;
816diff --git a/libsylph/ssl.c b/libsylph/ssl.c
817index 8413925..e782b0e 100644
818--- a/libsylph/ssl.c
819+++ b/libsylph/ssl.c
820@@ -32,6 +32,13 @@
821 #include "ssl.h"
822 #include "ssl_hostname_validation.h"
823
824+#define SALT_SIZE 16
825+#define CIPHER EVP_aes_256_cfb()
826+#define KEY_HASH EVP_sha256()
827+#define DIGEST_HASH EVP_sha256()
828+#define PBKDF2_DIGEST_SIZE 64
829+#define PBKDF2_ITERATIONS 100000
830+
831 static SSL_CTX *ssl_ctx_SSLv23 = NULL;
832 static SSL_CTX *ssl_ctx_TLSv1 = NULL;
833
834@@ -402,4 +409,468 @@ void ssl_set_verify_func(SSLVerifyFunc func)
835 verify_ui_func = func;
836 }
837
838+/* master password related functions */
839+static gint secure_derive_key(guchar *key,
840+ gint length_key,
841+ const gchar *passphrase,
842+ const guchar *salt) {
843+
844+ guint length_buffer, length_hash;
845+ guchar *buffer, *ptr_hash;
846+
847+ EVP_MD_CTX *mdctx;
848+
849+ OPENSSL_cleanse(key, length_key);
850+
851+ length_buffer = SALT_SIZE + strlen(passphrase);
852+ buffer = OPENSSL_malloc(length_buffer);
853+ OPENSSL_cleanse(buffer, length_buffer);
854+
855+ memcpy(buffer, salt, SALT_SIZE);
856+ memcpy(buffer + SALT_SIZE, passphrase, strlen(passphrase));
857+
858+ mdctx = EVP_MD_CTX_create();
859+ EVP_DigestInit_ex(mdctx, KEY_HASH, NULL);
860+ EVP_DigestUpdate(mdctx, buffer, length_buffer);
861+ OPENSSL_cleanse(buffer, length_buffer);
862+
863+ ptr_hash = OPENSSL_malloc(EVP_MD_size(KEY_HASH));
864+ EVP_DigestFinal_ex(mdctx, ptr_hash, &length_hash);
865+
866+ memcpy(key,
867+ ptr_hash,
868+ (length_hash > length_key) ? length_key : length_hash);
869+
870+ OPENSSL_cleanse(ptr_hash, length_hash);
871+ OPENSSL_free(ptr_hash);
872+ OPENSSL_free(buffer);
873+ EVP_MD_CTX_destroy(mdctx);
874+
875+ return SSL_RC_OK;
876+
877+}
878+
879+gint encrypt_data(gchar **encrypted,
880+ gint *length_encrypted,
881+ const gchar *data,
882+ const gchar *passphrase,
883+ gint length_data,
884+ guint min_data_length,
885+ gboolean rnd_salt) {
886+
887+ gint crypt_buffer_cnt, rc;
888+ guint key_size, length_hash;
889+ guint length_cleartext, length_ciphertext, length_total;
890+ guchar salt[SALT_SIZE];
891+ guchar *ciphertext_buffer, *total_buffer;
892+ /* sensitive buffers and counters */
893+ guint length_data_payload, length_padding;
894+ gchar str_data_size[3];
895+ guchar *data_payload_buffer, *hash_buffer, *padding_buffer, *key;
896+ guchar *cleartext_buffer;
897+
898+ EVP_CIPHER_CTX *ctx;
899+ EVP_MD_CTX *mdctx;
900+
901+ rc = SSL_RC_ERROR;
902+
903+ if (length_data < 1) {
904+ return -1;
905+ }
906+ if (rnd_salt) {
907+ if (RAND_bytes(salt, SALT_SIZE) != 1) {
908+ debug_print("Random problems...\n");
909+ goto cleanup;
910+ }
911+ } else {
912+ strncpy((gchar *)salt, "FOR TESTING ONLY", SALT_SIZE);
913+ }
914+
915+ key_size = EVP_CIPHER_key_length(CIPHER);
916+ key = OPENSSL_malloc(key_size);
917+ OPENSSL_cleanse(key, key_size);
918+ if (secure_derive_key(key,
919+ key_size,
920+ passphrase,
921+ salt) != SSL_RC_OK) {
922+ OPENSSL_cleanse(key, key_size);
923+ debug_print("Could not generate secure key\n");
924+ goto cleanup;
925+ }
926+
927+ /* prepare the data-buffer */
928+ length_padding = 0;
929+ if (length_data < min_data_length) {
930+ g_snprintf(str_data_size, 3, "%02d", length_data);
931+ length_padding = min_data_length - length_data;
932+ padding_buffer = OPENSSL_malloc(length_padding);
933+ OPENSSL_cleanse(padding_buffer, length_padding);
934+ if (RAND_bytes(padding_buffer, length_padding) != 1) {
935+ debug_print("Random problems...\n");
936+ goto cleanup;
937+ }
938+ } else {
939+ g_snprintf(str_data_size, 3, "-1");
940+ }
941+
942+ length_data_payload = 2 + length_data + length_padding;
943+ data_payload_buffer = OPENSSL_malloc(length_data_payload);
944+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
945+
946+ memcpy(data_payload_buffer, str_data_size, 2);
947+ memcpy(data_payload_buffer + 2, data, length_data);
948+ if (length_padding > 0) {
949+ memcpy(data_payload_buffer + (2 + length_data),
950+ padding_buffer,
951+ length_padding);
952+ }
953+
954+ mdctx = EVP_MD_CTX_create();
955+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
956+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
957+
958+ length_hash = EVP_MD_size(DIGEST_HASH);
959+ hash_buffer = OPENSSL_malloc(length_hash);
960+ OPENSSL_cleanse(hash_buffer, length_hash);
961+ EVP_DigestFinal_ex(mdctx, hash_buffer, NULL);
962+
963+ length_cleartext = length_hash + length_data_payload;
964+
965+ cleartext_buffer = OPENSSL_malloc(length_cleartext);
966+ OPENSSL_cleanse(cleartext_buffer, length_cleartext);
967+
968+ /* assemble cleartext-buffer */
969+ memcpy(cleartext_buffer, hash_buffer, length_hash);
970+ memcpy(cleartext_buffer + length_hash,
971+ data_payload_buffer,
972+ length_data_payload);
973+ OPENSSL_cleanse(data_payload_buffer, length_data_payload); /* sensitive */
974+
975+ /* encryption */
976+ if (!(ctx = EVP_CIPHER_CTX_new())) {
977+ debug_print("New ctx failed\n");
978+ goto cleanup;
979+ }
980+
981+ length_ciphertext = 0;
982+ if (EVP_EncryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
983+ debug_print("EVP_EncryptInit_ex failed\n");
984+ goto cleanup;
985+ }
986+
987+ ciphertext_buffer = OPENSSL_malloc(length_cleartext);
988+
989+ crypt_buffer_cnt = 0;
990+ while(1) {
991+ if (EVP_EncryptUpdate(ctx,
992+ ciphertext_buffer + length_ciphertext,
993+ &crypt_buffer_cnt,
994+ cleartext_buffer + length_ciphertext,
995+ 1) != 1) { /* one byte at a time */
996+ debug_print("EVP_EncryptUpdate failed\n");
997+ goto cleanup;
998+ }
999+
1000+ if (crypt_buffer_cnt != 1) { /* paranoia */
1001+ debug_print("The sizes of enc and dec text do not correspond\n");
1002+ goto cleanup;
1003+ }
1004+
1005+ ++length_ciphertext;
1006+
1007+ if (length_ciphertext >= length_cleartext) {
1008+ break;
1009+ }
1010+ }
1011+ /* No padding required for the CFB mode */
1012+
1013+ OPENSSL_cleanse(cleartext_buffer, length_cleartext); /* sensitive */
1014+ length_total = SALT_SIZE + length_ciphertext;
1015+ total_buffer = OPENSSL_malloc(length_total);
1016+
1017+ memcpy(total_buffer, salt, SALT_SIZE);
1018+ memcpy(total_buffer + SALT_SIZE, ciphertext_buffer, length_ciphertext);
1019+
1020+ *encrypted = g_base64_encode(total_buffer, length_total);
1021+ *length_encrypted = strlen(*encrypted);
1022+
1023+ rc = SSL_RC_OK;
1024+
1025+cleanup:
1026+ /* key */
1027+ OPENSSL_cleanse(key, key_size);
1028+ OPENSSL_free(key);
1029+ /* cleartext buffer */
1030+ OPENSSL_cleanse(cleartext_buffer, length_cleartext);
1031+ OPENSSL_free(cleartext_buffer);
1032+ /* payload buffer */
1033+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1034+ OPENSSL_free(data_payload_buffer);
1035+ /* hash */
1036+ OPENSSL_cleanse(hash_buffer, length_hash);
1037+ OPENSSL_free(hash_buffer);
1038+ /* padding */
1039+ if (length_padding > 0) {
1040+ OPENSSL_cleanse(padding_buffer, length_padding);
1041+ OPENSSL_free(padding_buffer);
1042+ }
1043+
1044+ OPENSSL_cleanse(str_data_size, 3); /* paranoia */
1045+
1046+ /* ciphertext buffer */
1047+ OPENSSL_free(ciphertext_buffer);
1048+
1049+ /* total buffer */
1050+ OPENSSL_free(total_buffer);
1051+
1052+ length_data_payload = 0;
1053+ length_padding = 0;
1054+
1055+ EVP_CIPHER_CTX_free(ctx);
1056+ EVP_MD_CTX_destroy(mdctx);
1057+
1058+ return rc;
1059+
1060+}
1061+
1062+gint decrypt_data(gchar **decrypted,
1063+ const gchar *data,
1064+ const gchar *passphrase,
1065+ gint length_data) {
1066+
1067+ gint rc; /* return code */
1068+ gint decrypt_buffer_cnt, length_ciphertext, length_decrypted;
1069+ guint key_size, length_hash, length_cleartext;
1070+ gsize length_total;
1071+ guchar salt[SALT_SIZE];
1072+ guchar *ciphertext_buffer, *total_buffer;
1073+ /* sensitive buffers and counters */
1074+ gint data_size;
1075+ guint length_data_payload;
1076+ gchar str_data_size[3];
1077+ guchar *data_payload_buffer, *hash_buffer, *key;
1078+ guchar *cleartext_buffer;
1079+
1080+ EVP_CIPHER_CTX *ctx;
1081+ EVP_MD_CTX *mdctx;
1082+
1083+ rc = -1;
1084+
1085+ if (length_data < 1) {
1086+ return -1;
1087+ }
1088+
1089+ total_buffer = g_base64_decode(data, &length_total);
1090+ length_ciphertext = length_total - SALT_SIZE;
1091+ ciphertext_buffer = OPENSSL_malloc(length_ciphertext);
1092+ OPENSSL_cleanse(ciphertext_buffer, length_ciphertext);
1093+
1094+ memcpy(salt, total_buffer, SALT_SIZE);
1095+ memcpy(ciphertext_buffer, total_buffer + SALT_SIZE, length_ciphertext);
1096+
1097+ key_size = EVP_CIPHER_key_length(CIPHER);
1098+
1099+ /* decryption */
1100+ if(!(ctx = EVP_CIPHER_CTX_new())) {
1101+ debug_print("New ctx failed\n");
1102+ goto cleanup;
1103+ }
1104+
1105+ key = OPENSSL_malloc(key_size);
1106+ OPENSSL_cleanse(key, key_size);
1107+ if (secure_derive_key(key,
1108+ key_size,
1109+ passphrase,
1110+ salt) != 0) {
1111+ OPENSSL_cleanse(key, key_size);
1112+ debug_print("Could not generate secure key\n");
1113+ goto cleanup;
1114+ }
1115+
1116+ if (EVP_DecryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
1117+ debug_print("EVP_DecryptInit_ex failed\n");
1118+ goto cleanup;
1119+ }
1120+ OPENSSL_cleanse(key, key_size); /* highly sensitive */
1121+ cleartext_buffer = OPENSSL_malloc(length_ciphertext);
1122+ OPENSSL_cleanse(cleartext_buffer, length_ciphertext);
1123+
1124+ length_cleartext = 0;
1125+ decrypt_buffer_cnt = 0;
1126+
1127+ while(1) {
1128+ if (EVP_DecryptUpdate(ctx,
1129+ cleartext_buffer + length_cleartext,
1130+ &decrypt_buffer_cnt,
1131+ ciphertext_buffer + length_cleartext,
1132+ 1) != 1) { /* one byte at a time */
1133+ debug_print("EVP_EncryptUpdate failed\n");
1134+ goto cleanup;
1135+ }
1136+
1137+ if (decrypt_buffer_cnt != 1) { /* paranoia */
1138+ debug_print("The sizes of enc and dec text do not correspond");
1139+ goto cleanup;
1140+ }
1141+
1142+ ++length_cleartext;
1143+
1144+ if (length_cleartext >= length_ciphertext) {
1145+ break;
1146+ }
1147+ }
1148+
1149+
1150+ length_hash = EVP_MD_size(DIGEST_HASH);
1151+ hash_buffer = OPENSSL_malloc(length_hash);
1152+ length_data_payload = length_cleartext - length_hash;
1153+ data_payload_buffer = OPENSSL_malloc(length_data_payload);
1154+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1155+
1156+ memcpy(data_payload_buffer,
1157+ cleartext_buffer + length_hash,
1158+ length_data_payload);
1159+
1160+ mdctx = EVP_MD_CTX_create();
1161+ EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
1162+ EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
1163+ EVP_DigestFinal_ex(mdctx, hash_buffer, &length_hash);
1164+
1165+ if (strncmp((const gchar*) hash_buffer,
1166+ (const gchar*) cleartext_buffer,
1167+ length_hash) != 0) {
1168+ debug_print("Invalid hash\n");
1169+ rc = SSL_RC_WRONG_HASH_OR_KEY;
1170+ goto cleanup;
1171+ }
1172+
1173+ memcpy(str_data_size, cleartext_buffer + length_hash, 2);
1174+ data_size = atoi(str_data_size);
1175+
1176+ if (data_size < 0) {
1177+ length_decrypted = length_data_payload - 2;
1178+ } else {
1179+ length_decrypted = data_size;
1180+ }
1181+ *decrypted = OPENSSL_malloc(length_decrypted);
1182+ memcpy(*decrypted, data_payload_buffer + 2, length_decrypted);
1183+
1184+ rc = SSL_RC_OK;
1185+
1186+cleanup:
1187+
1188+ /* key */
1189+ OPENSSL_cleanse(key, key_size);
1190+ OPENSSL_free(key);
1191+ /* payload buffer */
1192+ OPENSSL_cleanse(data_payload_buffer, length_data_payload);
1193+ OPENSSL_free(data_payload_buffer);
1194+ /* hash */
1195+ OPENSSL_cleanse(hash_buffer, length_hash);
1196+ OPENSSL_free(hash_buffer);
1197+ /* ciphertext */
1198+ OPENSSL_free(ciphertext_buffer);
1199+ OPENSSL_free(total_buffer);
1200+
1201+ EVP_CIPHER_CTX_free(ctx);
1202+ EVP_MD_CTX_destroy(mdctx);
1203+
1204+ return rc;
1205+
1206+}
1207+
1208+gint generate_password_hash(gchar **password_hash,
1209+ const gchar *password,
1210+ const guchar *salt) {
1211+ /*
1212+ * Hashes 'password' using PKCS5_PBKDF2_HMAC with SHA512 and 'salt',
1213+ * and assigns a string to 'password_hash' with the following format:
1214+ * pbkdf2_sha512$iterations$base64(salt)$base64(password_hash)
1215+ */
1216+ guchar lsalt[SALT_SIZE];
1217+ gchar *PBKDF2_digest, *salt_b64, *digest_b64;
1218+
1219+ if (salt == NULL) {
1220+ if (RAND_bytes(lsalt, SALT_SIZE) != 1) {
1221+ debug_print("Random problems...\n");
1222+ return SSL_RC_ERROR;
1223+ }
1224+ } else {
1225+ memcpy(lsalt, salt, SALT_SIZE);
1226+ }
1227+
1228+ PBKDF2_digest = OPENSSL_malloc(PBKDF2_DIGEST_SIZE);
1229+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1230+
1231+ PKCS5_PBKDF2_HMAC(password,
1232+ strlen(password),
1233+ lsalt,
1234+ SALT_SIZE,
1235+ PBKDF2_ITERATIONS,
1236+ EVP_sha512(),
1237+ PBKDF2_DIGEST_SIZE,
1238+ (guchar *) PBKDF2_digest);
1239+ digest_b64 = g_base64_encode((guchar *) PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1240+ OPENSSL_cleanse(PBKDF2_digest, PBKDF2_DIGEST_SIZE);
1241+ OPENSSL_free(PBKDF2_digest);
1242+
1243+ salt_b64 = g_base64_encode(lsalt, SALT_SIZE);
1244+
1245+ *password_hash = g_strdup_printf("pbkdf2_sha512$%d$%s$%s",
1246+ PBKDF2_ITERATIONS,
1247+ salt_b64,
1248+ digest_b64);
1249+
1250+ OPENSSL_cleanse(digest_b64, strlen(digest_b64));
1251+ OPENSSL_free(digest_b64);
1252+
1253+ OPENSSL_cleanse(salt_b64, strlen(salt_b64));
1254+ OPENSSL_free(salt_b64);
1255+
1256+ return SSL_RC_OK;
1257+
1258+}
1259+
1260+gint check_password(const gchar *password, const gchar *password_hash) {
1261+
1262+ gint token_counter, rc;
1263+ guchar *salt;
1264+ gchar **tokens, *new_hash;
1265+ gsize salt_length;
1266+
1267+ rc = SSL_RC_ERROR;
1268+ tokens = g_strsplit(password_hash,
1269+ "$",
1270+ -1);
1271+ token_counter = 0;
1272+ while (*(tokens + token_counter) != NULL) {
1273+ ++token_counter;
1274+ }
1275+
1276+ if (token_counter != 4) {
1277+ debug_print("Invalid password hash...\n");
1278+ goto cleanup;
1279+ }
1280+
1281+ salt = g_base64_decode(*(tokens + 2), &salt_length);
1282+ if (salt_length != SALT_SIZE) {
1283+ debug_print("Salt size does not match\n");
1284+ goto cleanup;
1285+ }
1286+
1287+ if (generate_password_hash(&new_hash, password, salt) != SSL_RC_OK) {
1288+ debug_print("Password hash generation failed\n");
1289+ goto cleanup;
1290+ }
1291+
1292+ rc = g_strcmp0(password_hash, new_hash);
1293+
1294+cleanup:
1295+ g_free(salt);
1296+ g_free(new_hash);
1297+ g_strfreev(tokens);
1298+ return rc;
1299+
1300+}
1301+
1302 #endif /* USE_SSL */
1303diff --git a/libsylph/ssl.h b/libsylph/ssl.h
1304index a9f690d..4c0ccd1 100644
1305--- a/libsylph/ssl.h
1306+++ b/libsylph/ssl.h
1307@@ -32,9 +32,15 @@
1308 #include <openssl/pem.h>
1309 #include <openssl/ssl.h>
1310 #include <openssl/err.h>
1311+#include <openssl/evp.h>
1312+#include <openssl/rand.h>
1313
1314 #include "socket.h"
1315
1316+#define SSL_RC_OK 0
1317+#define SSL_RC_ERROR -1
1318+#define SSL_RC_WRONG_HASH_OR_KEY 1
1319+
1320 typedef enum {
1321 SSL_METHOD_SSLv23,
1322 SSL_METHOD_TLSv1
1323@@ -60,6 +66,26 @@ void ssl_done_socket (SockInfo *sockinfo);
1324
1325 void ssl_set_verify_func (SSLVerifyFunc func);
1326
1327+/* master password related code */
1328+gint encrypt_data(gchar **encrypted,
1329+ gint *length_encrypted,
1330+ const gchar *data,
1331+ const gchar *passphrase,
1332+ gint length_data,
1333+ guint min_data_length,
1334+ gboolean rnd_salt);
1335+
1336+gint decrypt_data(gchar **decrypted,
1337+ const gchar *data,
1338+ const gchar *passphrase,
1339+ gint length_data);
1340+
1341+gint generate_password_hash(gchar **password_hash,
1342+ const gchar *password,
1343+ const guchar *salt);
1344+
1345+gint check_password(const gchar *password, const gchar *password_hash);
1346+/* ---------------------------- */
1347 #endif /* USE_SSL */
1348
1349 #endif /* __SSL_H__ */
1350diff --git a/libsylph/utils.c b/libsylph/utils.c
1351index aabce06..3bbcbcf 100644
1352--- a/libsylph/utils.c
1353+++ b/libsylph/utils.c
1354@@ -3306,10 +3306,10 @@ gint canonicalize_file(const gchar *src, const gchar *dest)
1355 }
1356 }
1357
1358- if (last_linebreak == TRUE) {
1359- if (fputs("\r\n", dest_fp) == EOF)
1360- err = TRUE;
1361- }
1362+ /* if (last_linebreak == TRUE) { */
1363+ /* if (fputs("\r\n", dest_fp) == EOF) */
1364+ /* err = TRUE; */
1365+ /* } */
1366
1367 if (ferror(src_fp)) {
1368 FILE_OP_ERROR(src, "fgets");
1369@@ -4598,6 +4598,36 @@ gchar *input_query_password(const gchar *server, const gchar *user)
1370 return NULL;
1371 }
1372
1373+static QueryMasterPasswordFunc query_master_password_func = NULL;
1374+
1375+void set_input_query_master_password_func(QueryMasterPasswordFunc func)
1376+{
1377+ query_master_password_func = func;
1378+}
1379+
1380+gchar *input_query_master_password(void)
1381+{
1382+ if (query_master_password_func)
1383+ return query_master_password_func();
1384+ else
1385+ return NULL;
1386+}
1387+
1388+static SetNewPasswordFunc set_new_password_func = NULL;
1389+
1390+void set_input_set_new_password_func(SetNewPasswordFunc func)
1391+{
1392+ set_new_password_func = func;
1393+}
1394+
1395+gchar *input_set_new_password(guint max_attempts)
1396+{
1397+ if (set_new_password_func)
1398+ return set_new_password_func(max_attempts);
1399+ else
1400+ return NULL;
1401+}
1402+
1403 /* logging */
1404
1405 static FILE *log_fp = NULL;
1406diff --git a/libsylph/utils.h b/libsylph/utils.h
1407index 9ac65cf..ede55ff 100644
1408--- a/libsylph/utils.h
1409+++ b/libsylph/utils.h
1410@@ -202,6 +202,8 @@ typedef void (*ProgressFunc) (gint cur,
1411 gint total);
1412 typedef gchar * (*QueryPasswordFunc) (const gchar *server,
1413 const gchar *user);
1414+typedef gchar * (*QueryMasterPasswordFunc) (void);
1415+typedef gchar * (*SetNewPasswordFunc) (guint max_attempts);
1416 typedef void (*LogFunc) (const gchar *str);
1417 typedef void (*LogFlushFunc) (void);
1418
1419@@ -560,9 +562,12 @@ void progress_show (gint cur,
1420
1421 /* user input */
1422 void set_input_query_password_func (QueryPasswordFunc func);
1423-
1424 gchar *input_query_password (const gchar *server,
1425 const gchar *user);
1426+void set_input_query_master_password_func(QueryMasterPasswordFunc func);
1427+gchar *input_query_master_password(void);
1428+void set_input_set_new_password_func(SetNewPasswordFunc func);
1429+gchar *input_set_new_password(guint max_attempts);
1430
1431 /* logging */
1432 void set_log_file (const gchar *filename);
1433diff --git a/src/inputdialog.c b/src/inputdialog.c
1434index a601085..cdfb997 100644
1435--- a/src/inputdialog.c
1436+++ b/src/inputdialog.c
1437@@ -44,6 +44,7 @@
1438 #include "filesel.h"
1439 #include "prefs_common.h"
1440 #include "gtkutils.h"
1441+#include "masterpassword.h"
1442 #include "utils.h"
1443
1444 #define DIALOG_WIDTH 420
1445@@ -156,6 +157,57 @@ gchar *input_dialog_query_password(const gchar *server, const gchar *user)
1446 return pass;
1447 }
1448
1449+gchar *input_dialog_query_master_password(void)
1450+{
1451+ gchar *mp_input;
1452+
1453+ mp_input = input_dialog_with_invisible(_("Input password"),
1454+ _("Master password"),
1455+ NULL);
1456+
1457+ if (prefs_common.use_master_password &&
1458+ prefs_common.auto_unload_master_password)
1459+ {
1460+ g_timeout_add(1000 * 30, unload_master_password, NULL);
1461+ debug_print("Master password to be unloaded in 30 seconds\n");
1462+ }
1463+
1464+ return mp_input;
1465+}
1466+
1467+gchar *input_dialog_set_new_password(guint max_attempts)
1468+{
1469+ guint cnt;
1470+ gchar *pass1, *pass2;
1471+
1472+ if (max_attempts < 1)
1473+ return NULL;
1474+
1475+ for (cnt = 0; cnt < max_attempts; ++cnt) {
1476+ pass1 = input_dialog_with_invisible(
1477+ _("Input password"),
1478+ _("New password"),
1479+ NULL);
1480+ pass2 = input_dialog_with_invisible(
1481+ _("Input password"),
1482+ _("Confirm new password"),
1483+ NULL);
1484+
1485+ if (pass1 != NULL && pass2 != NULL && strcmp(pass1, pass2) == 0) {
1486+ cleanse_buffer(pass2, strlen(pass2));
1487+ g_free(pass2);
1488+ break;
1489+ }
1490+ cleanse_buffer(pass1, strlen(pass1));
1491+ cleanse_buffer(pass2, strlen(pass2));
1492+ g_free(pass2);
1493+ g_free(pass1);
1494+ pass1 = NULL;
1495+ }
1496+
1497+ return pass1;
1498+}
1499+
1500 gchar *input_dialog_with_filesel(const gchar *title, const gchar *message,
1501 const gchar *default_string,
1502 GtkFileChooserAction action)
1503diff --git a/src/inputdialog.h b/src/inputdialog.h
1504index 02bdda3..5364d1a 100644
1505--- a/src/inputdialog.h
1506+++ b/src/inputdialog.h
1507@@ -36,7 +36,8 @@ gchar *input_dialog_combo (const gchar *title,
1508 gboolean case_sensitive);
1509 gchar *input_dialog_query_password (const gchar *server,
1510 const gchar *user);
1511-
1512+gchar *input_dialog_query_master_password(void);
1513+gchar *input_dialog_set_new_password(guint max_attempts);
1514 gchar *input_dialog_with_filesel (const gchar *title,
1515 const gchar *message,
1516 const gchar *default_string,
1517diff --git a/src/main.c b/src/main.c
1518index 77d8192..97a4bd0 100644
1519--- a/src/main.c
1520+++ b/src/main.c
1521@@ -87,6 +87,7 @@
1522 #include "foldersel.h"
1523 #include "update_check.h"
1524 #include "colorlabel.h"
1525+#include "masterpassword.h"
1526
1527 #if USE_GPGME
1528 # include "rfc2015.h"
1529@@ -265,14 +266,54 @@ int main(int argc, char *argv[])
1530 set_ui_update_func(gtkut_events_flush);
1531 set_progress_func(main_window_progress_show);
1532 set_input_query_password_func(input_dialog_query_password);
1533+ set_input_set_new_password_func(input_dialog_set_new_password);
1534 #if USE_SSL
1535 ssl_init();
1536 ssl_set_verify_func(ssl_manager_verify_cert);
1537+ set_input_query_master_password_func(input_dialog_query_master_password);
1538 #endif
1539
1540 CHDIR_EXIT_IF_FAIL(get_home_dir(), 1);
1541
1542 prefs_common_read_config();
1543+ set_master_password(NULL);
1544+#if USE_SSL
1545+ if (prefs_common.use_master_password) {
1546+ if (prefs_common.master_password_hash != NULL) {
1547+ if (check_master_password_interactively(3) != MP_RC_OK) {
1548+ if (alertpanel(_("Master password"),
1549+ _("Invalid master password"),
1550+ GTK_STOCK_DISCARD,
1551+ GTK_STOCK_QUIT,
1552+ NULL) != G_ALERTDEFAULT) {
1553+ exit(1);
1554+ }
1555+ }
1556+ } else {
1557+ alertpanel_notice(
1558+ _("Master password enabled but not set. Setting one now"));
1559+ if (set_master_password_interactively(3) != MP_RC_OK) {
1560+ if (alertpanel(_("Master password"),
1561+ _("Unable to set master password"),
1562+ GTK_STOCK_DISCARD,
1563+ GTK_STOCK_QUIT,
1564+ NULL) != G_ALERTDEFAULT) {
1565+ exit(1);
1566+ }
1567+ }
1568+ }
1569+ }
1570+ /* security goal:
1571+ * if the master password is enabled and loaded on init,
1572+ * an attacker can potentially set use_master_password - disabled
1573+ * afterwards and then force Sylpheed to save account data - the result
1574+ * being passwords saved to accountrc in plain-text.
1575+ * possible solution:
1576+ * Do not allow the passwords to be stored in plain-text if Sylpheed
1577+ * was started with use_master_password - enabled.
1578+ */
1579+ master_password_enabled_on_init = prefs_common.use_master_password;
1580+#endif
1581 filter_set_addressbook_func(addressbook_has_address);
1582 filter_read_config();
1583 prefs_actions_read_config();
1584@@ -381,6 +422,13 @@ int main(int argc, char *argv[])
1585
1586 remote_command_exec();
1587
1588+#if USE_SSL
1589+ if (prefs_common.auto_unload_master_password && master_password_active()) {
1590+ debug_print("Auto unloading master password\n");
1591+ unload_master_password();
1592+ }
1593+#endif
1594+
1595 #if USE_UPDATE_CHECK
1596 if (prefs_common.auto_update_check)
1597 update_check(FALSE);
1598@@ -993,6 +1041,7 @@ void app_will_exit(gboolean force)
1599
1600 /* remove temporary files, close log file, socket cleanup */
1601 #if USE_SSL
1602+ unload_master_password();
1603 ssl_done();
1604 #endif
1605 syl_cleanup();
1606diff --git a/src/prefs_account_dialog.c b/src/prefs_account_dialog.c
1607index e9cba13..e215b8e 100644
1608--- a/src/prefs_account_dialog.c
1609+++ b/src/prefs_account_dialog.c
1610@@ -267,7 +267,7 @@ static PrefsUIData ui_data[] = {
1611 {"user_id", &basic.uid_entry,
1612 prefs_set_data_from_entry, prefs_set_entry},
1613 {"password", &basic.pass_entry,
1614- prefs_set_data_from_entry, prefs_set_entry},
1615+ prefs_set_data_from_epass_entry, prefs_set_entry},
1616
1617 /* Receive */
1618 {"use_apop_auth", &receive.use_apop_chkbtn,
1619@@ -313,7 +313,7 @@ static PrefsUIData ui_data[] = {
1620 {"smtp_user_id", &p_send.smtp_uid_entry,
1621 prefs_set_data_from_entry, prefs_set_entry},
1622 {"smtp_password", &p_send.smtp_pass_entry,
1623- prefs_set_data_from_entry, prefs_set_entry},
1624+ prefs_set_data_from_epass_entry, prefs_set_entry},
1625
1626 {"pop_before_smtp", &p_send.pop_bfr_smtp_chkbtn,
1627 prefs_set_data_from_toggle, prefs_set_toggle},
1628diff --git a/src/prefs_common_dialog.c b/src/prefs_common_dialog.c
1629index b46c9b7..119a056 100644
1630--- a/src/prefs_common_dialog.c
1631+++ b/src/prefs_common_dialog.c
1632@@ -205,6 +205,7 @@ static struct JunkMail {
1633 static struct Privacy {
1634 GtkWidget *checkbtn_auto_check_signatures;
1635 GtkWidget *checkbtn_gpg_signature_popup;
1636+ GtkWidget *radiobtn_gpg_signature_all;
1637 GtkWidget *checkbtn_store_passphrase;
1638 GtkWidget *spinbtn_store_passphrase;
1639 GtkObject *spinbtn_store_passphrase_adj;
1640@@ -215,6 +216,14 @@ static struct Privacy {
1641 } privacy;
1642 #endif
1643
1644+#if USE_SSL
1645+static struct MasterPassword {
1646+ GtkWidget *checkbtn_use_master_password;
1647+ GtkWidget *checkbtn_auto_unload_master_password;
1648+ GtkWidget *spinbtn_encrypted_password_min_length;
1649+} master_password;
1650+#endif
1651+
1652 static struct Interface {
1653 GtkWidget *checkbtn_always_show_msg;
1654 GtkWidget *checkbtn_always_mark_read;
1655@@ -314,6 +323,13 @@ static void prefs_common_attach_toolbtn_pos_set_radiobtn (PrefParam *pparam);
1656 static void prefs_common_online_mode_set_data_from_radiobtn(PrefParam *pparam);
1657 static void prefs_common_online_mode_set_radiobtn (PrefParam *pparam);
1658
1659+#if USE_GPGME
1660+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn(
1661+ PrefParam *pparam);
1662+static void prefs_common_gpg_signature_popup_mode_set_radiobtn(
1663+ PrefParam *pparam);
1664+#endif
1665+
1666 static PrefsUIData ui_data[] = {
1667 /* Receive */
1668 {"autochk_newmail", &receive.checkbtn_autochk,
1669@@ -540,6 +556,9 @@ static PrefsUIData ui_data[] = {
1670 prefs_set_data_from_toggle, prefs_set_toggle},
1671 {"gpg_signature_popup", &privacy.checkbtn_gpg_signature_popup,
1672 prefs_set_data_from_toggle, prefs_set_toggle},
1673+ {"gpg_signature_popup_mode", &privacy.radiobtn_gpg_signature_all,
1674+ prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn,
1675+ prefs_common_gpg_signature_popup_mode_set_radiobtn},
1676 {"store_passphrase", &privacy.checkbtn_store_passphrase,
1677 prefs_set_data_from_toggle, prefs_set_toggle},
1678 {"store_passphrase_timeout", &privacy.spinbtn_store_passphrase,
1679@@ -552,6 +571,18 @@ static PrefsUIData ui_data[] = {
1680 prefs_set_data_from_toggle, prefs_set_toggle},
1681 #endif /* USE_GPGME */
1682
1683+#if USE_SSL
1684+ {"use_master_password", &master_password.checkbtn_use_master_password,
1685+ prefs_set_data_from_toggle, prefs_set_toggle},
1686+ {"auto_unload_master_password",
1687+ &master_password.checkbtn_auto_unload_master_password,
1688+ prefs_set_data_from_toggle, prefs_set_toggle},
1689+ {"encrypted_password_min_length",
1690+ &master_password.spinbtn_encrypted_password_min_length,
1691+ prefs_set_data_from_spinbtn,
1692+ prefs_set_spinbtn},
1693+#endif
1694+
1695 /* Interface */
1696 {"always_show_message_when_selected",
1697 &iface.checkbtn_always_show_msg,
1698@@ -682,6 +713,9 @@ static void prefs_junk_create (void);
1699 #if USE_GPGME
1700 static void prefs_privacy_create (void);
1701 #endif
1702+#if USE_SSL
1703+static void prefs_master_password_create (void);
1704+#endif
1705 static void prefs_details_create (void);
1706 static GtkWidget *prefs_other_create (void);
1707 static GtkWidget *prefs_extcmd_create (void);
1708@@ -841,6 +875,10 @@ static void prefs_common_create(void)
1709 #if USE_GPGME
1710 prefs_privacy_create();
1711 SET_NOTEBOOK_LABEL(dialog.notebook, _("Privacy"), page++);
1712+#endif
1713+#if USE_SSL
1714+ prefs_master_password_create();
1715+ SET_NOTEBOOK_LABEL(dialog.notebook, _("Master password"), page++);
1716 #endif
1717 prefs_details_create();
1718 SET_NOTEBOOK_LABEL(dialog.notebook, _("Details"), page++);
1719@@ -2478,10 +2516,14 @@ static void prefs_privacy_create(void)
1720 GtkWidget *vbox2;
1721 GtkWidget *vbox3;
1722 GtkWidget *hbox1;
1723+ GtkWidget *vbox_sign_popup_mode;
1724+ GtkWidget *hbox_sign_popup_mode;
1725 GtkWidget *hbox_spc;
1726 GtkWidget *label;
1727 GtkWidget *checkbtn_auto_check_signatures;
1728 GtkWidget *checkbtn_gpg_signature_popup;
1729+ GtkWidget *radiobtn_gpg_signature_all;
1730+ GtkWidget *radiobtn_gpg_signature_bad;
1731 GtkWidget *checkbtn_store_passphrase;
1732 GtkObject *spinbtn_store_passphrase_adj;
1733 GtkWidget *spinbtn_store_passphrase;
1734@@ -2505,6 +2547,56 @@ static void prefs_privacy_create(void)
1735 PACK_CHECK_BUTTON (vbox2, checkbtn_gpg_signature_popup,
1736 _("Show signature check result in a popup window"));
1737
1738+ vbox_sign_popup_mode = gtk_vbox_new (FALSE, VSPACING_NARROW);
1739+ gtk_widget_show (vbox_sign_popup_mode);
1740+ gtk_box_pack_start (GTK_BOX (vbox2), vbox_sign_popup_mode, FALSE, FALSE, 0);
1741+
1742+ hbox_sign_popup_mode = gtk_hbox_new (FALSE, 8);
1743+ gtk_widget_show (hbox_sign_popup_mode);
1744+ gtk_box_pack_start (GTK_BOX (vbox_sign_popup_mode),
1745+ hbox_sign_popup_mode,
1746+ FALSE,
1747+ FALSE,
1748+ 0);
1749+
1750+ hbox_spc = gtk_hbox_new (FALSE, 0);
1751+ gtk_widget_show (hbox_spc);
1752+ gtk_box_pack_start (GTK_BOX (hbox_sign_popup_mode),
1753+ hbox_spc,
1754+ FALSE,
1755+ FALSE,
1756+ 0);
1757+ gtk_widget_set_size_request (hbox_spc, 12, -1);
1758+
1759+ radiobtn_gpg_signature_all = gtk_radio_button_new_with_label(
1760+ NULL,
1761+ _("All signatures"));
1762+ gtk_widget_show(radiobtn_gpg_signature_all);
1763+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode),
1764+ radiobtn_gpg_signature_all,
1765+ FALSE,
1766+ FALSE,
1767+ 0);
1768+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_all),
1769+ MENU_VAL_ID,
1770+ GINT_TO_POINTER (1));
1771+
1772+ radiobtn_gpg_signature_bad = gtk_radio_button_new_with_label_from_widget(
1773+ GTK_RADIO_BUTTON (radiobtn_gpg_signature_all),
1774+ _("Bad signatures only"));
1775+ gtk_widget_show(radiobtn_gpg_signature_bad);
1776+ gtk_box_pack_start(GTK_BOX (hbox_sign_popup_mode),
1777+ radiobtn_gpg_signature_bad,
1778+ FALSE,
1779+ FALSE,
1780+ 0);
1781+ g_object_set_data(G_OBJECT (radiobtn_gpg_signature_bad),
1782+ MENU_VAL_ID,
1783+ GINT_TO_POINTER (0));
1784+
1785+ SET_TOGGLE_SENSITIVITY (checkbtn_gpg_signature_popup,
1786+ hbox_sign_popup_mode);
1787+
1788 PACK_CHECK_BUTTON (vbox2, checkbtn_store_passphrase,
1789 _("Store passphrase in memory temporarily"));
1790
1791@@ -2572,7 +2664,8 @@ static void prefs_privacy_create(void)
1792 = checkbtn_auto_check_signatures;
1793 privacy.checkbtn_gpg_signature_popup
1794 = checkbtn_gpg_signature_popup;
1795- privacy.checkbtn_store_passphrase = checkbtn_store_passphrase;
1796+ privacy.radiobtn_gpg_signature_all = radiobtn_gpg_signature_all;
1797+ privacy.checkbtn_store_passphrase = checkbtn_store_passphrase;
1798 privacy.spinbtn_store_passphrase = spinbtn_store_passphrase;
1799 privacy.spinbtn_store_passphrase_adj = spinbtn_store_passphrase_adj;
1800 #ifndef G_OS_WIN32
1801@@ -2582,6 +2675,93 @@ static void prefs_privacy_create(void)
1802 }
1803 #endif /* USE_GPGME */
1804
1805+#if USE_SSL
1806+static void prefs_master_password_create(void)
1807+{
1808+ GtkWidget *vbox_main;
1809+ GtkWidget *vbox_master_password_options;
1810+ GtkWidget *vbox_master_password_suboptions;
1811+ GtkWidget *hbox1;
1812+ GtkWidget *hbox_spc;
1813+ GtkWidget *label;
1814+ GtkWidget *checkbtn_use_master_password;
1815+ GtkWidget *checkbtn_auto_unload_master_password;
1816+ GtkWidget *spinbtn_encrypted_password_min_length;
1817+ GtkObject *spinbtn_encrypted_password_min_length_adj;
1818+
1819+ vbox_main = gtk_vbox_new (FALSE, VSPACING);
1820+ gtk_widget_show (vbox_main);
1821+ gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox_main);
1822+ gtk_container_set_border_width (GTK_CONTAINER (vbox_main), VBOX_BORDER);
1823+
1824+ vbox_master_password_options = gtk_vbox_new (FALSE, 0);
1825+ gtk_widget_show (vbox_master_password_options);
1826+ gtk_box_pack_start (
1827+ GTK_BOX (vbox_main), vbox_master_password_options, FALSE, FALSE, 0);
1828+
1829+ PACK_CHECK_BUTTON (vbox_master_password_options,
1830+ checkbtn_use_master_password,
1831+ _("Use master password"));
1832+
1833+ vbox_master_password_suboptions = gtk_vbox_new (FALSE, VSPACING_NARROW);
1834+ gtk_widget_show (vbox_master_password_suboptions);
1835+ gtk_box_pack_start (GTK_BOX (vbox_master_password_options),
1836+ vbox_master_password_suboptions,
1837+ FALSE,
1838+ FALSE,
1839+ 0);
1840+
1841+ PACK_CHECK_BUTTON (vbox_master_password_suboptions,
1842+ checkbtn_auto_unload_master_password,
1843+ _("Automatically unload master password "
1844+ "after session initialization"));
1845+
1846+ hbox1 = gtk_hbox_new (FALSE, 8);
1847+ gtk_widget_show (hbox1);
1848+ gtk_box_pack_start (GTK_BOX (vbox_master_password_suboptions),
1849+ hbox1,
1850+ FALSE,
1851+ FALSE,
1852+ 0);
1853+
1854+ hbox_spc = gtk_hbox_new (FALSE, 0);
1855+ gtk_widget_show (hbox_spc);
1856+ gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0);
1857+ gtk_widget_set_size_request (hbox_spc, 12, -1);
1858+
1859+ label = gtk_label_new (_("Min. password length"));
1860+ gtk_widget_show (label);
1861+ gtk_box_pack_start (
1862+ GTK_BOX (hbox1), label, FALSE, FALSE, 0);
1863+
1864+ spinbtn_encrypted_password_min_length_adj = gtk_adjustment_new (
1865+ 32, 0, 99, 1, 5, 0);
1866+ spinbtn_encrypted_password_min_length = gtk_spin_button_new(
1867+ GTK_ADJUSTMENT (spinbtn_encrypted_password_min_length_adj), 1, 0);
1868+ gtk_widget_show (spinbtn_encrypted_password_min_length);
1869+ gtk_box_pack_start (GTK_BOX (hbox1),
1870+ spinbtn_encrypted_password_min_length,
1871+ FALSE,
1872+ FALSE,
1873+ 0);
1874+ gtk_spin_button_set_numeric (
1875+ GTK_SPIN_BUTTON (spinbtn_encrypted_password_min_length), TRUE);
1876+ gtk_widget_set_size_request (spinbtn_encrypted_password_min_length,
1877+ 64,
1878+ -1);
1879+
1880+ SET_TOGGLE_SENSITIVITY (checkbtn_use_master_password,
1881+ vbox_master_password_suboptions);
1882+
1883+ master_password.checkbtn_use_master_password
1884+ = checkbtn_use_master_password;
1885+ master_password.checkbtn_auto_unload_master_password
1886+ = checkbtn_auto_unload_master_password;
1887+ master_password.spinbtn_encrypted_password_min_length
1888+ = spinbtn_encrypted_password_min_length;
1889+}
1890+#endif /* USE_SSL */
1891+
1892 static void prefs_details_create(void)
1893 {
1894 GtkWidget *vbox1;
1895@@ -4719,6 +4899,60 @@ static void prefs_common_online_mode_set_radiobtn(PrefParam *pparam)
1896 }
1897 }
1898
1899+#if USE_GPGME
1900+static void prefs_common_gpg_signature_popup_mode_set_data_from_radiobtn(
1901+ PrefParam *pparam)
1902+{
1903+ PrefsUIData *ui_data;
1904+ GtkRadioButton *radiobtn;
1905+ GSList *group;
1906+
1907+ ui_data = (PrefsUIData *)pparam->ui_data;
1908+ g_return_if_fail(ui_data != NULL);
1909+ g_return_if_fail(*ui_data->widget != NULL);
1910+
1911+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget);
1912+ group = gtk_radio_button_get_group(radiobtn);
1913+ while (group != NULL) {
1914+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data);
1915+
1916+ if (gtk_toggle_button_get_active(btn)) {
1917+ prefs_common.gpg_signature_popup_mode =
1918+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), MENU_VAL_ID));
1919+ break;
1920+ }
1921+ group = group->next;
1922+ }
1923+}
1924+
1925+static void prefs_common_gpg_signature_popup_mode_set_radiobtn(
1926+ PrefParam *pparam)
1927+{
1928+ PrefsUIData *ui_data;
1929+ GtkRadioButton *radiobtn;
1930+ GSList *group;
1931+
1932+ ui_data = (PrefsUIData *)pparam->ui_data;
1933+ g_return_if_fail(ui_data != NULL);
1934+ g_return_if_fail(*ui_data->widget != NULL);
1935+
1936+ radiobtn = GTK_RADIO_BUTTON(*ui_data->widget);
1937+ group = gtk_radio_button_get_group(radiobtn);
1938+ while (group != NULL) {
1939+ GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data);
1940+ gint data;
1941+
1942+ data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn),
1943+ MENU_VAL_ID));
1944+ if (data == prefs_common.gpg_signature_popup_mode) {
1945+ gtk_toggle_button_set_active(btn, TRUE);
1946+ break;
1947+ }
1948+ group = group->next;
1949+ }
1950+}
1951+#endif
1952+
1953 static void prefs_common_dispitem_clicked(void)
1954 {
1955 prefs_summary_column_open(FOLDER_ITEM_IS_SENT_FOLDER
1956diff --git a/src/prefs_ui.c b/src/prefs_ui.c
1957index a3a8f74..2ab1d45 100644
1958--- a/src/prefs_ui.c
1959+++ b/src/prefs_ui.c
1960@@ -31,11 +31,13 @@
1961 #include <errno.h>
1962
1963 #include "prefs.h"
1964+#include "prefs_common.h"
1965 #include "prefs_ui.h"
1966 #include "menu.h"
1967 #include "codeconv.h"
1968 #include "utils.h"
1969 #include "gtkutils.h"
1970+#include "masterpassword.h"
1971
1972 typedef enum
1973 {
1974@@ -268,6 +270,65 @@ void prefs_set_data_from_entry(PrefParam *pparam)
1975 }
1976 }
1977
1978+void prefs_set_data_from_epass_entry(PrefParam *pparam)
1979+{
1980+#if USE_SSL
1981+ PrefsUIData *ui_data;
1982+ gchar **str;
1983+ const gchar *entry_str;
1984+
1985+ /* This is where decrypted passwords are encrypted and stored again */
1986+
1987+ /* master_password == NULL for any of the following reasons:
1988+ * - use_master_password is not enabled (we just save without encrypting)
1989+ * - master_password is auto unloaded (prompt for the master password)
1990+ * - undefined reason (refure to store the password)
1991+ */
1992+ if (!prefs_common.use_master_password) {
1993+ /* check if use_master_password was enabled when Sylpheed started */
1994+ if (!master_password_enabled_on_init) {
1995+ prefs_set_data_from_entry(pparam);
1996+ }
1997+ return;
1998+ } else if (master_password == NULL) {
1999+ if (!prefs_common.auto_unload_master_password) {
2000+ debug_print("Master password enabled, but not loaded for no "
2001+ "apparent reason. Not storing\n");
2002+ return;
2003+ } else {
2004+ if (check_master_password_interactively(3) != MP_RC_OK) {
2005+ debug_print("Failed to reload the master password\n");
2006+ return;
2007+ }
2008+ }
2009+ }
2010+
2011+ ui_data = (PrefsUIData *)pparam->ui_data;
2012+ g_return_if_fail(ui_data != NULL);
2013+ g_return_if_fail(*ui_data->widget != NULL);
2014+
2015+ entry_str = gtk_entry_get_text(GTK_ENTRY(*ui_data->widget));
2016+
2017+ switch (pparam->type) {
2018+ case P_STRING:
2019+ str = (gchar **)pparam->data;
2020+ g_free(*str);
2021+ if ((entry_str != NULL) && (!mpes_string_prefix(entry_str))) {
2022+ debug_print("Encrypting GTK password entry\n");
2023+ *str = encrypt_with_master_password(entry_str);
2024+ } else {
2025+ *str = entry_str[0] ? g_strdup(entry_str) : NULL;
2026+ }
2027+ break;
2028+ default:
2029+ g_warning("Invalid PrefType for GtkEntry widget: %d\n",
2030+ pparam->type);
2031+ }
2032+#else
2033+ prefs_set_data_from_entry(pparam);
2034+#endif
2035+}
2036+
2037 void prefs_set_entry(PrefParam *pparam)
2038 {
2039 PrefsUIData *ui_data;
2040diff --git a/src/prefs_ui.h b/src/prefs_ui.h
2041index cfdf353..f7bd903 100644
2042--- a/src/prefs_ui.h
2043+++ b/src/prefs_ui.h
2044@@ -164,6 +164,7 @@ void prefs_set_data_from_dialog (PrefParam *param);
2045 void prefs_set_dialog_to_default(PrefParam *param);
2046
2047 void prefs_set_data_from_entry (PrefParam *pparam);
2048+void prefs_set_data_from_epass_entry(PrefParam *pparam);
2049 void prefs_set_entry (PrefParam *pparam);
2050 void prefs_set_data_from_text (PrefParam *pparam);
2051 void prefs_set_text (PrefParam *pparam);
2052diff --git a/src/rfc2015.c b/src/rfc2015.c
2053index ebfa96c..48825e2 100644
2054--- a/src/rfc2015.c
2055+++ b/src/rfc2015.c
2056@@ -210,8 +210,12 @@ static void check_signature(MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
2057 gchar *tmp_file;
2058 gint n_exclude_chars = 0;
2059
2060- if (prefs_common.gpg_signature_popup)
2061+ if (prefs_common.gpg_signature_popup &&
2062+ prefs_common.gpg_signature_popup_mode == 1)
2063+ {
2064+ /* FIXME: Perhaps some macro instead of 1 */
2065 statuswindow = gpgmegtk_sig_status_create();
2066+ }
2067
2068 err = gpgme_new(&ctx);
2069 if (err) {
2070@@ -309,8 +313,21 @@ leave:
2071 result = _("Error verifying the signature");
2072 }
2073 debug_print("verification status: %s\n", result);
2074- if (prefs_common.gpg_signature_popup)
2075+ if (prefs_common.gpg_signature_popup &&
2076+ prefs_common.gpg_signature_popup_mode == 1)
2077+ {
2078+ /* FIXME: Perhaps some macro instead of 1 */
2079+ gpgmegtk_sig_status_update(statuswindow, ctx);
2080+ } else if (prefs_common.gpg_signature_popup &&
2081+ verifyresult->signatures->status != GPG_ERR_NO_DATA &&
2082+ verifyresult->signatures->status != GPG_ERR_NO_ERROR)
2083+ {
2084+ /* prefs_common.gpg_signature_popup_mode == 0 is useless as long as
2085+ * there are only two modes (0 and 1)
2086+ */
2087+ statuswindow = gpgmegtk_sig_status_create();
2088 gpgmegtk_sig_status_update(statuswindow, ctx);
2089+ }
2090
2091 g_free (partinfo->sigstatus);
2092 partinfo->sigstatus = g_strdup (result);
2093@@ -319,8 +336,11 @@ leave:
2094 gpgme_data_release(text);
2095 if (ctx)
2096 gpgme_release(ctx);
2097- if (prefs_common.gpg_signature_popup)
2098+ if (prefs_common.gpg_signature_popup) {
2099+ /* gpgmegtk_sig_status_destroy handles NULL params so no complicated
2100+ check is needed */
2101 gpgmegtk_sig_status_destroy(statuswindow);
2102+ }
2103 }
2104
2105 /*
2106@@ -427,7 +447,11 @@ static gpgme_data_t pgp_decrypt(MsgInfo *msginfo, MimeInfo *partinfo, FILE *fp)
2107 debug_print("verification status: %s\n", result);
2108 debug_print("full status: %s\n",
2109 msginfo->encinfo->sigstatus_full);
2110- if (prefs_common.gpg_signature_popup) {
2111+ if (prefs_common.gpg_signature_popup &&
2112+ ((prefs_common.gpg_signature_popup_mode == 1) ||
2113+ (verifyresult->signatures->status != GPG_ERR_NO_DATA &&
2114+ verifyresult->signatures->status != GPG_ERR_NO_ERROR)))
2115+ {
2116 GpgmegtkSigStatus statuswindow;
2117 statuswindow = gpgmegtk_sig_status_create();
2118 gpgmegtk_sig_status_update(statuswindow, ctx);
2119diff --git a/src/send_message.c b/src/send_message.c
2120index a8c2c7a..537c3c8 100644
2121--- a/src/send_message.c
2122+++ b/src/send_message.c
2123@@ -58,6 +58,7 @@
2124 #include "inc.h"
2125 #include "mainwindow.h"
2126 #include "summaryview.h"
2127+#include "masterpassword.h"
2128
2129 #define SMTP_PORT 25
2130 #if USE_SSL
2131@@ -648,13 +649,13 @@ static gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp)
2132
2133 if (ac_prefs->smtp_userid) {
2134 smtp_session->user = g_strdup(ac_prefs->smtp_userid);
2135- if (ac_prefs->smtp_passwd)
2136- smtp_session->pass =
2137- g_strdup(ac_prefs->smtp_passwd);
2138- else if (ac_prefs->tmp_smtp_pass)
2139+ if (ac_prefs->smtp_passwd) {
2140+ smtp_session->pass = decrypt_with_master_password(
2141+ ac_prefs->smtp_passwd);
2142+ } else if (ac_prefs->tmp_smtp_pass) {
2143 smtp_session->pass =
2144 g_strdup(ac_prefs->tmp_smtp_pass);
2145- else {
2146+ } else {
2147 smtp_session->pass =
2148 input_query_password
2149 (ac_prefs->smtp_server,