summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2018-04-15 15:40:25 +0200
committerSimeon Simeonov2018-04-15 15:40:25 +0200
commit7addc67cd3f6a4c0358f70b4fb9906ebcd88b9e3 (patch)
tree6bdc8ffd8c4e42cf289df1271660172366468743
parent46275eef3de4e025b5657e67e142fc58125779be (diff)
Add support for auto-unloading of the master password
-rw-r--r--libsylph/masterpassword.c37
-rw-r--r--libsylph/masterpassword.h5
-rw-r--r--libsylph/ssl.c20
-rw-r--r--libsylph/ssl.h6
-rw-r--r--src/main.c14
-rw-r--r--src/prefs_ui.c27
6 files changed, 62 insertions, 47 deletions
diff --git a/libsylph/masterpassword.c b/libsylph/masterpassword.c
index 8174def..bac49c4 100644
--- a/libsylph/masterpassword.c
+++ b/libsylph/masterpassword.c
@@ -9,7 +9,7 @@
9 * 9 *
10 * This library is distributed in the hope that it will be useful, 10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details. 13 * Lesser General Public License for more details.
14 * 14 *
15 * You should have received a copy of the GNU Lesser General Public 15 * You should have received a copy of the GNU Lesser General Public
@@ -30,6 +30,7 @@
30 30
31 31
32gchar *master_password; 32gchar *master_password;
33gboolean master_password_enabled_on_init;
33 34
34void set_master_password(const char *password) { 35void set_master_password(const char *password) {
35 master_password = password; 36 master_password = password;
@@ -91,7 +92,7 @@ gchar *decrypt_with_master_password(const gchar *str) {
91 if (master_password == NULL) { 92 if (master_password == NULL) {
92 /* we have empty or auto unloaded master password */ 93 /* we have empty or auto unloaded master password */
93 if ((!prefs_common.auto_unload_master_password) || 94 if ((!prefs_common.auto_unload_master_password) ||
94 (check_master_password_interactively(3) != RC_OK)) { 95 (check_master_password_interactively(3) != MP_RC_OK)) {
95 return g_strdup(str); 96 return g_strdup(str);
96 } 97 }
97 debug_print("Reloaded master password\n"); 98 debug_print("Reloaded master password\n");
@@ -104,7 +105,7 @@ gchar *decrypt_with_master_password(const gchar *str) {
104 if (decrypt_data(&new_str, 105 if (decrypt_data(&new_str,
105 str + str_prefix, 106 str + str_prefix,
106 master_password, 107 master_password,
107 strlen(str) - str_prefix) != RC_OK) { 108 strlen(str) - str_prefix) != MP_RC_OK) {
108 OPENSSL_cleanse(new_str, strlen(new_str)); 109 OPENSSL_cleanse(new_str, strlen(new_str));
109 g_free(new_str); 110 g_free(new_str);
110 return g_strdup(str); 111 return g_strdup(str);
@@ -123,17 +124,13 @@ gchar *encrypt_with_master_password(const gchar *str) {
123 gchar *new_str, *mpes1_str; 124 gchar *new_str, *mpes1_str;
124 gint length_encrypted; 125 gint length_encrypted;
125 126
126 if ((!str) || (!prefs_common.use_master_password)) 127 if ((!str) || (!master_password_active()))
127 return g_strdup(str); 128 return NULL;
128 129
129 if (master_password == NULL) { 130 /*
130 /* we have empty or auto unloaded master password */ 131 * unlike the decrypt function, here it is up to the caller
131 if ((!prefs_common.auto_unload_master_password) || 132 * to make sure that auto unloaded master password is handled properly
132 (check_master_password_interactively(3) != RC_OK)) { 133 */
133 return g_strdup(str);
134 }
135 debug_print("Reloaded master password\n");
136 }
137 134
138 if (encrypt_data(&new_str, 135 if (encrypt_data(&new_str,
139 &length_encrypted, 136 &length_encrypted,
@@ -141,19 +138,17 @@ gchar *encrypt_with_master_password(const gchar *str) {
141 master_password, 138 master_password,
142 strlen(str), 139 strlen(str),
143 prefs_common.encrypted_password_min_length, 140 prefs_common.encrypted_password_min_length,
144 TRUE) != RC_OK) { 141 TRUE) != MP_RC_OK) {
145 OPENSSL_cleanse(new_str, strlen(new_str));
146 g_free(new_str); 142 g_free(new_str);
147 return g_strdup(str); 143 return NULL;
148 } 144 }
149 145
150 mpes1_str = g_strdup_printf("mpes1:%s", new_str); 146 mpes1_str = g_strdup_printf("mpes1:%s", new_str);
151 OPENSSL_cleanse(new_str, strlen(new_str));
152 g_free(new_str); 147 g_free(new_str);
153 148
154 return mpes1_str; 149 return mpes1_str;
155#else 150#else
156 return g_strdup(str); 151 return NULL;
157#endif 152#endif
158 153
159} 154}
@@ -170,7 +165,7 @@ gint set_master_password_interactively(guint max_attempts) {
170 if (generate_password_hash( 165 if (generate_password_hash(
171 &prefs_common.master_password_hash, 166 &prefs_common.master_password_hash,
172 master_password, 167 master_password,
173 NULL) != RC_OK) { 168 NULL) != MP_RC_OK) {
174 /* should not really happen unless buggy code / library */ 169 /* should not really happen unless buggy code / library */
175 g_free(prefs_common.master_password_hash); 170 g_free(prefs_common.master_password_hash);
176 prefs_common.master_password_hash = NULL; 171 prefs_common.master_password_hash = NULL;
@@ -204,8 +199,8 @@ gint check_master_password_interactively(guint max_attempts) {
204 continue; 199 continue;
205 } 200 }
206 if (check_password(master_password, 201 if (check_password(master_password,
207 prefs_common.master_password_hash) == RC_OK) { 202 prefs_common.master_password_hash) == MP_RC_OK) {
208 return RC_OK; /* match */ 203 return MP_RC_OK; /* match */
209 } 204 }
210 debug_print(_("Wrong master password entered (%d)\n"), cnt); 205 debug_print(_("Wrong master password entered (%d)\n"), cnt);
211 OPENSSL_cleanse(master_password, strlen(master_password)); 206 OPENSSL_cleanse(master_password, strlen(master_password));
diff --git a/libsylph/masterpassword.h b/libsylph/masterpassword.h
index cc32358..7254643 100644
--- a/libsylph/masterpassword.h
+++ b/libsylph/masterpassword.h
@@ -24,7 +24,12 @@
24#include "config.h" 24#include "config.h"
25#endif 25#endif
26 26
27#define MP_RC_OK 0
28#define MP_RC_WRONG_HASH_OR_KEY 1
29#define MP_RC_INVALID_FORMAT 2 /* invalid digest format */
30
27extern gchar *master_password; 31extern gchar *master_password;
32extern gboolean master_password_enabled_on_init; /* m.p. enabled on init? */
28void set_master_password(const char *password); 33void set_master_password(const char *password);
29gchar *get_master_password(void); 34gchar *get_master_password(void);
30void cleanse_buffer(void *buf, size_t len); 35void cleanse_buffer(void *buf, size_t len);
diff --git a/libsylph/ssl.c b/libsylph/ssl.c
index 44b2935..e782b0e 100644
--- a/libsylph/ssl.c
+++ b/libsylph/ssl.c
@@ -446,7 +446,7 @@ static gint secure_derive_key(guchar *key,
446 OPENSSL_free(buffer); 446 OPENSSL_free(buffer);
447 EVP_MD_CTX_destroy(mdctx); 447 EVP_MD_CTX_destroy(mdctx);
448 448
449 return RC_OK; 449 return SSL_RC_OK;
450 450
451} 451}
452 452
@@ -472,7 +472,7 @@ gint encrypt_data(gchar **encrypted,
472 EVP_CIPHER_CTX *ctx; 472 EVP_CIPHER_CTX *ctx;
473 EVP_MD_CTX *mdctx; 473 EVP_MD_CTX *mdctx;
474 474
475 rc = RC_ERROR; 475 rc = SSL_RC_ERROR;
476 476
477 if (length_data < 1) { 477 if (length_data < 1) {
478 return -1; 478 return -1;
@@ -492,7 +492,7 @@ gint encrypt_data(gchar **encrypted,
492 if (secure_derive_key(key, 492 if (secure_derive_key(key,
493 key_size, 493 key_size,
494 passphrase, 494 passphrase,
495 salt) != RC_OK) { 495 salt) != SSL_RC_OK) {
496 OPENSSL_cleanse(key, key_size); 496 OPENSSL_cleanse(key, key_size);
497 debug_print("Could not generate secure key\n"); 497 debug_print("Could not generate secure key\n");
498 goto cleanup; 498 goto cleanup;
@@ -594,7 +594,7 @@ gint encrypt_data(gchar **encrypted,
594 *encrypted = g_base64_encode(total_buffer, length_total); 594 *encrypted = g_base64_encode(total_buffer, length_total);
595 *length_encrypted = strlen(*encrypted); 595 *length_encrypted = strlen(*encrypted);
596 596
597 rc = RC_OK; 597 rc = SSL_RC_OK;
598 598
599cleanup: 599cleanup:
600 /* key */ 600 /* key */
@@ -740,7 +740,7 @@ gint decrypt_data(gchar **decrypted,
740 (const gchar*) cleartext_buffer, 740 (const gchar*) cleartext_buffer,
741 length_hash) != 0) { 741 length_hash) != 0) {
742 debug_print("Invalid hash\n"); 742 debug_print("Invalid hash\n");
743 rc = RC_WRONG_HASH_OR_KEY; 743 rc = SSL_RC_WRONG_HASH_OR_KEY;
744 goto cleanup; 744 goto cleanup;
745 } 745 }
746 746
@@ -755,7 +755,7 @@ gint decrypt_data(gchar **decrypted,
755 *decrypted = OPENSSL_malloc(length_decrypted); 755 *decrypted = OPENSSL_malloc(length_decrypted);
756 memcpy(*decrypted, data_payload_buffer + 2, length_decrypted); 756 memcpy(*decrypted, data_payload_buffer + 2, length_decrypted);
757 757
758 rc = RC_OK; 758 rc = SSL_RC_OK;
759 759
760cleanup: 760cleanup:
761 761
@@ -793,7 +793,7 @@ gint generate_password_hash(gchar **password_hash,
793 if (salt == NULL) { 793 if (salt == NULL) {
794 if (RAND_bytes(lsalt, SALT_SIZE) != 1) { 794 if (RAND_bytes(lsalt, SALT_SIZE) != 1) {
795 debug_print("Random problems...\n"); 795 debug_print("Random problems...\n");
796 return RC_ERROR; 796 return SSL_RC_ERROR;
797 } 797 }
798 } else { 798 } else {
799 memcpy(lsalt, salt, SALT_SIZE); 799 memcpy(lsalt, salt, SALT_SIZE);
@@ -827,7 +827,7 @@ gint generate_password_hash(gchar **password_hash,
827 OPENSSL_cleanse(salt_b64, strlen(salt_b64)); 827 OPENSSL_cleanse(salt_b64, strlen(salt_b64));
828 OPENSSL_free(salt_b64); 828 OPENSSL_free(salt_b64);
829 829
830 return RC_OK; 830 return SSL_RC_OK;
831 831
832} 832}
833 833
@@ -838,7 +838,7 @@ gint check_password(const gchar *password, const gchar *password_hash) {
838 gchar **tokens, *new_hash; 838 gchar **tokens, *new_hash;
839 gsize salt_length; 839 gsize salt_length;
840 840
841 rc = RC_ERROR; 841 rc = SSL_RC_ERROR;
842 tokens = g_strsplit(password_hash, 842 tokens = g_strsplit(password_hash,
843 "$", 843 "$",
844 -1); 844 -1);
@@ -858,7 +858,7 @@ gint check_password(const gchar *password, const gchar *password_hash) {
858 goto cleanup; 858 goto cleanup;
859 } 859 }
860 860
861 if (generate_password_hash(&new_hash, password, salt) != RC_OK) { 861 if (generate_password_hash(&new_hash, password, salt) != SSL_RC_OK) {
862 debug_print("Password hash generation failed\n"); 862 debug_print("Password hash generation failed\n");
863 goto cleanup; 863 goto cleanup;
864 } 864 }
diff --git a/libsylph/ssl.h b/libsylph/ssl.h
index 6338911..4c0ccd1 100644
--- a/libsylph/ssl.h
+++ b/libsylph/ssl.h
@@ -37,9 +37,9 @@
37 37
38#include "socket.h" 38#include "socket.h"
39 39
40#define RC_OK 0 40#define SSL_RC_OK 0
41#define RC_ERROR -1 41#define SSL_RC_ERROR -1
42#define RC_WRONG_HASH_OR_KEY 1 42#define SSL_RC_WRONG_HASH_OR_KEY 1
43 43
44typedef enum { 44typedef enum {
45 SSL_METHOD_SSLv23, 45 SSL_METHOD_SSLv23,
diff --git a/src/main.c b/src/main.c
index 3c87c70..97a4bd0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -280,7 +280,7 @@ int main(int argc, char *argv[])
280#if USE_SSL 280#if USE_SSL
281 if (prefs_common.use_master_password) { 281 if (prefs_common.use_master_password) {
282 if (prefs_common.master_password_hash != NULL) { 282 if (prefs_common.master_password_hash != NULL) {
283 if (check_master_password_interactively(3) != RC_OK) { 283 if (check_master_password_interactively(3) != MP_RC_OK) {
284 if (alertpanel(_("Master password"), 284 if (alertpanel(_("Master password"),
285 _("Invalid master password"), 285 _("Invalid master password"),
286 GTK_STOCK_DISCARD, 286 GTK_STOCK_DISCARD,
@@ -292,7 +292,7 @@ int main(int argc, char *argv[])
292 } else { 292 } else {
293 alertpanel_notice( 293 alertpanel_notice(
294 _("Master password enabled but not set. Setting one now")); 294 _("Master password enabled but not set. Setting one now"));
295 if (set_master_password_interactively(3) != RC_OK) { 295 if (set_master_password_interactively(3) != MP_RC_OK) {
296 if (alertpanel(_("Master password"), 296 if (alertpanel(_("Master password"),
297 _("Unable to set master password"), 297 _("Unable to set master password"),
298 GTK_STOCK_DISCARD, 298 GTK_STOCK_DISCARD,
@@ -303,6 +303,16 @@ int main(int argc, char *argv[])
303 } 303 }
304 } 304 }
305 } 305 }
306 /* security goal:
307 * if the master password is enabled and loaded on init,
308 * an attacker can potentially set use_master_password - disabled
309 * afterwards and then force Sylpheed to save account data - the result
310 * being passwords saved to accountrc in plain-text.
311 * possible solution:
312 * Do not allow the passwords to be stored in plain-text if Sylpheed
313 * was started with use_master_password - enabled.
314 */
315 master_password_enabled_on_init = prefs_common.use_master_password;
306#endif 316#endif
307 filter_set_addressbook_func(addressbook_has_address); 317 filter_set_addressbook_func(addressbook_has_address);
308 filter_read_config(); 318 filter_read_config();
diff --git a/src/prefs_ui.c b/src/prefs_ui.c
index 051a058..2ab1d45 100644
--- a/src/prefs_ui.c
+++ b/src/prefs_ui.c
@@ -279,23 +279,28 @@ void prefs_set_data_from_epass_entry(PrefParam *pparam)
279 279
280 /* This is where decrypted passwords are encrypted and stored again */ 280 /* This is where decrypted passwords are encrypted and stored again */
281 281
282 /* TODO: A potential exploit is disabling master password and then forcing
283 * Sylpheed to store the password
284 */
285
286 /* master_password == NULL for any of the following reasons: 282 /* master_password == NULL for any of the following reasons:
287 * - use_master_password is not enabled (we just save without encrypting) 283 * - use_master_password is not enabled (we just save without encrypting)
288 * - master_password is auto unloaded (let the encrypt function handle it) 284 * - master_password is auto unloaded (prompt for the master password)
289 * - undefined reason (refure to store the password) 285 * - undefined reason (refure to store the password)
290 */ 286 */
291 if (!prefs_common.use_master_password) { 287 if (!prefs_common.use_master_password) {
292 prefs_set_data_from_entry(pparam); 288 /* check if use_master_password was enabled when Sylpheed started */
293 return; 289 if (!master_password_enabled_on_init) {
294 } else if ((master_password == NULL) && 290 prefs_set_data_from_entry(pparam);
295 (!prefs_common.auto_unload_master_password)) { 291 }
296 debug_print("Master password enabled, but not loaded for no "
297 "apparent reason. Not storing\n");
298 return; 292 return;
293 } else if (master_password == NULL) {
294 if (!prefs_common.auto_unload_master_password) {
295 debug_print("Master password enabled, but not loaded for no "
296 "apparent reason. Not storing\n");
297 return;
298 } else {
299 if (check_master_password_interactively(3) != MP_RC_OK) {
300 debug_print("Failed to reload the master password\n");
301 return;
302 }
303 }
299 } 304 }
300 305
301 ui_data = (PrefsUIData *)pparam->ui_data; 306 ui_data = (PrefsUIData *)pparam->ui_data;