diff options
| -rw-r--r-- | libsylph/masterpassword.c | 37 | ||||
| -rw-r--r-- | libsylph/masterpassword.h | 5 | ||||
| -rw-r--r-- | libsylph/ssl.c | 20 | ||||
| -rw-r--r-- | libsylph/ssl.h | 6 | ||||
| -rw-r--r-- | src/main.c | 14 | ||||
| -rw-r--r-- | src/prefs_ui.c | 27 |
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 | ||
| 32 | gchar *master_password; | 32 | gchar *master_password; |
| 33 | gboolean master_password_enabled_on_init; | ||
| 33 | 34 | ||
| 34 | void set_master_password(const char *password) { | 35 | void 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 | |||
| 27 | extern gchar *master_password; | 31 | extern gchar *master_password; |
| 32 | extern gboolean master_password_enabled_on_init; /* m.p. enabled on init? */ | ||
| 28 | void set_master_password(const char *password); | 33 | void set_master_password(const char *password); |
| 29 | gchar *get_master_password(void); | 34 | gchar *get_master_password(void); |
| 30 | void cleanse_buffer(void *buf, size_t len); | 35 | void 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 | ||
| 599 | cleanup: | 599 | cleanup: |
| 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 | ||
| 760 | cleanup: | 760 | cleanup: |
| 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 | ||
| 44 | typedef enum { | 44 | typedef enum { |
| 45 | SSL_METHOD_SSLv23, | 45 | SSL_METHOD_SSLv23, |
| @@ -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; |
