summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-27 21:52:12 +0100
committerSimeon Simeonov2018-02-27 21:52:12 +0100
commit1bb2a9d33e8cd193c21f9995fb7852c7562cafcf (patch)
treebf54cba95a7b7afa68b33f8cb40b53d351b880ff
parent3f7a3add4dc0814e01c3032d73a4cdd29372f8d4 (diff)
First prototype
-rw-r--r--libsylph/account.c7
-rw-r--r--libsylph/defs.h3
-rw-r--r--libsylph/imap.c12
-rw-r--r--libsylph/prefs_account.c1
-rw-r--r--libsylph/prefs_account.h2
-rw-r--r--libsylph/ssl.c377
-rw-r--r--libsylph/ssl.h20
-rw-r--r--src/send_message.c20
8 files changed, 436 insertions, 6 deletions
diff --git a/libsylph/account.c b/libsylph/account.c
index 2431bf3..de0d6f0 100644
--- a/libsylph/account.c
+++ b/libsylph/account.c
@@ -51,6 +51,10 @@ void account_read_config_all(void)
51 FILE *fp; 51 FILE *fp;
52 gchar buf[PREFSBUFSIZE]; 52 gchar buf[PREFSBUFSIZE];
53 PrefsAccount *ac_prefs; 53 PrefsAccount *ac_prefs;
54#if USE_SSL
55 gchar *master_password;
56 master_password = input_query_password("Sylpheed", "Master password");
57#endif
54 58
55 debug_print(_("Reading all config for each account...\n")); 59 debug_print(_("Reading all config for each account...\n"));
56 60
@@ -79,6 +83,9 @@ void account_read_config_all(void)
79 for (cur = ac_label_list; cur != NULL; cur = cur->next) { 83 for (cur = ac_label_list; cur != NULL; cur = cur->next) {
80 ac_prefs = prefs_account_new(); 84 ac_prefs = prefs_account_new();
81 prefs_account_read_config(ac_prefs, (gchar *)cur->data); 85 prefs_account_read_config(ac_prefs, (gchar *)cur->data);
86#if USE_SSL
87 ac_prefs->master_password = master_password;
88#endif
82 account_list = g_list_append(account_list, ac_prefs); 89 account_list = g_list_append(account_list, ac_prefs);
83 if (ac_prefs->is_default) 90 if (ac_prefs->is_default)
84 cur_account = ac_prefs; 91 cur_account = ac_prefs;
diff --git a/libsylph/defs.h b/libsylph/defs.h
index 9e3f82b..67325bf 100644
--- a/libsylph/defs.h
+++ b/libsylph/defs.h
@@ -58,6 +58,9 @@
58#define DISPLAY_HEADER_RC "dispheaderrc" 58#define DISPLAY_HEADER_RC "dispheaderrc"
59#define MENU_RC "menurc" 59#define MENU_RC "menurc"
60#define ACTIONS_RC "actionsrc" 60#define ACTIONS_RC "actionsrc"
61#ifdef USE_SSL
62#define SECURE_RC "securerc"
63#endif
61#define COMMAND_HISTORY "command_history" 64#define COMMAND_HISTORY "command_history"
62#define TEMPLATE_DIR "templates" 65#define TEMPLATE_DIR "templates"
63#define TMP_DIR "tmp" 66#define TMP_DIR "tmp"
diff --git a/libsylph/imap.c b/libsylph/imap.c
index aa0d737..a61dc2b 100644
--- a/libsylph/imap.c
+++ b/libsylph/imap.c
@@ -707,7 +707,17 @@ static gint imap_session_connect(IMAPSession *session)
707 log_message(_("creating IMAP4 connection to %s:%d ...\n"), 707 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
708 SESSION(session)->server, SESSION(session)->port); 708 SESSION(session)->server, SESSION(session)->port);
709 709
710 pass = account->passwd; 710 if (account->master_password != NULL) {
711 if (decrypt_data(&pass,
712 account->passwd,
713 account->master_password,
714 strlen(account->passwd)) != RC_OK) {
715 session_destroy(session);
716 return -1;
717 }
718 } else {
719 pass = account->passwd;
720 }
711 if (!pass) 721 if (!pass)
712 pass = account->tmp_pass; 722 pass = account->tmp_pass;
713 if (!pass) { 723 if (!pass) {
diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c
index 1aecba9..a16ba3d 100644
--- a/libsylph/prefs_account.c
+++ b/libsylph/prefs_account.c
@@ -218,6 +218,7 @@ void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label)
218 g_free(rcpath); 218 g_free(rcpath);
219 219
220 *ac_prefs = tmp_ac_prefs; 220 *ac_prefs = tmp_ac_prefs;
221 ac_prefs->master_password = NULL;
221 while (*p && !g_ascii_isdigit(*p)) p++; 222 while (*p && !g_ascii_isdigit(*p)) p++;
222 id = atoi(p); 223 id = atoi(p);
223 if (id < 0) g_warning("wrong account id: %d\n", id); 224 if (id < 0) g_warning("wrong account id: %d\n", id);
diff --git a/libsylph/prefs_account.h b/libsylph/prefs_account.h
index ad899f8..5239034 100644
--- a/libsylph/prefs_account.h
+++ b/libsylph/prefs_account.h
@@ -187,6 +187,8 @@ struct _PrefsAccount
187 /* Compose */ 187 /* Compose */
188 gchar *sig_names[10]; 188 gchar *sig_names[10];
189 gchar *sig_texts[10]; 189 gchar *sig_texts[10];
190
191 gchar *master_password;
190}; 192};
191 193
192PrefsAccount *prefs_account_new (void); 194PrefsAccount *prefs_account_new (void);
diff --git a/libsylph/ssl.c b/libsylph/ssl.c
index 8413925..5a76b8b 100644
--- a/libsylph/ssl.c
+++ b/libsylph/ssl.c
@@ -32,6 +32,11 @@
32#include "ssl.h" 32#include "ssl.h"
33#include "ssl_hostname_validation.h" 33#include "ssl_hostname_validation.h"
34 34
35#define SALT_SIZE 16
36#define CIPHER EVP_aes_256_cfb()
37#define KEY_HASH EVP_sha256()
38#define DIGEST_HASH EVP_sha256()
39
35static SSL_CTX *ssl_ctx_SSLv23 = NULL; 40static SSL_CTX *ssl_ctx_SSLv23 = NULL;
36static SSL_CTX *ssl_ctx_TLSv1 = NULL; 41static SSL_CTX *ssl_ctx_TLSv1 = NULL;
37 42
@@ -402,4 +407,376 @@ void ssl_set_verify_func(SSLVerifyFunc func)
402 verify_ui_func = func; 407 verify_ui_func = func;
403} 408}
404 409
410/* master password related functions */
411static gint secure_derive_key(guchar *key,
412 gint length_key,
413 const gchar *passphrase,
414 const guchar *salt) {
415
416 guint length_buffer, length_hash;
417 guchar *buffer, *ptr_hash;
418
419 EVP_MD_CTX *mdctx;
420
421 OPENSSL_cleanse(key, length_key);
422
423 length_buffer = SALT_SIZE + strlen(passphrase);
424 buffer = OPENSSL_malloc(length_buffer);
425 OPENSSL_cleanse(buffer, length_buffer);
426
427 memcpy(buffer, salt, SALT_SIZE);
428 memcpy(buffer + SALT_SIZE, passphrase, strlen(passphrase));
429
430 mdctx = EVP_MD_CTX_create();
431 EVP_DigestInit_ex(mdctx, KEY_HASH, NULL);
432 EVP_DigestUpdate(mdctx, buffer, length_buffer);
433 OPENSSL_cleanse(buffer, length_buffer);
434
435 ptr_hash = OPENSSL_malloc(EVP_MD_size(KEY_HASH));
436 EVP_DigestFinal_ex(mdctx, ptr_hash, &length_hash);
437
438 memcpy(key,
439 ptr_hash,
440 (length_hash > length_key) ? length_key : length_hash);
441
442 OPENSSL_cleanse(ptr_hash, length_hash);
443 OPENSSL_free(ptr_hash);
444 OPENSSL_free(buffer);
445 EVP_MD_CTX_destroy(mdctx);
446
447 return RC_OK;
448
449}
450
451
452gint encrypt_data(gchar **encrypted,
453 gint *length_encrypted,
454 const gchar *data,
455 const gchar *passphrase,
456 gint length_data,
457 guint min_data_length,
458 gboolean rnd_salt) {
459
460 gint crypt_buffer_cnt, rc;
461 guint key_size, length_hash;
462 guint length_cleartext, length_ciphertext, length_total;
463 guchar salt[SALT_SIZE];
464 guchar *ciphertext_buffer, *total_buffer;
465 /* sensitive buffers and counters */
466 guint length_data_payload, length_padding;
467 gchar str_data_size[3];
468 guchar *data_payload_buffer, *hash_buffer, *padding_buffer, *key;
469 guchar *cleartext_buffer;
470
471 EVP_CIPHER_CTX *ctx;
472 EVP_MD_CTX *mdctx;
473
474 rc = RC_ERROR;
475
476 if (length_data < 1) {
477 return -1;
478 }
479 if (rnd_salt) {
480 if (RAND_bytes(salt, SALT_SIZE) != 1) {
481 debug_print("Random problems...\n");
482 goto cleanup;
483 }
484 } else {
485 strncpy((gchar *)salt, "FOR TESTING ONLY", SALT_SIZE);
486 }
487
488 key_size = EVP_CIPHER_key_length(CIPHER);
489 key = OPENSSL_malloc(key_size);
490 OPENSSL_cleanse(key, key_size);
491 if (secure_derive_key(key,
492 key_size,
493 passphrase,
494 salt) != RC_OK) {
495 OPENSSL_cleanse(key, key_size);
496 debug_print("Could not generate secure key\n");
497 goto cleanup;
498 }
499
500 /* prepare the data-buffer */
501 length_padding = 0;
502 if (length_data < min_data_length) {
503 g_snprintf(str_data_size, 3, "%02d", length_data);
504 length_padding = min_data_length - length_data;
505 padding_buffer = OPENSSL_malloc(length_padding);
506 OPENSSL_cleanse(padding_buffer, length_padding);
507 if (RAND_bytes(padding_buffer, length_padding) != 1) {
508 debug_print("Random problems...\n");
509 goto cleanup;
510 }
511 } else {
512 g_snprintf(str_data_size, 3, "-1");
513 }
514
515 length_data_payload = 2 + length_data + length_padding;
516 data_payload_buffer = OPENSSL_malloc(length_data_payload);
517 OPENSSL_cleanse(data_payload_buffer, length_data_payload);
518
519 memcpy(data_payload_buffer, str_data_size, 2);
520 memcpy(data_payload_buffer + 2, data, length_data);
521 if (length_padding > 0) {
522 memcpy(data_payload_buffer + (2 + length_data),
523 padding_buffer,
524 length_padding);
525 }
526
527 mdctx = EVP_MD_CTX_create();
528 EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
529 EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
530
531 length_hash = EVP_MD_size(DIGEST_HASH);
532 hash_buffer = OPENSSL_malloc(length_hash);
533 OPENSSL_cleanse(hash_buffer, length_hash);
534 EVP_DigestFinal_ex(mdctx, hash_buffer, NULL);
535
536 length_cleartext = length_hash + length_data_payload;
537
538 cleartext_buffer = OPENSSL_malloc(length_cleartext);
539 OPENSSL_cleanse(cleartext_buffer, length_cleartext);
540
541 /* assemble cleartext-buffer */
542 memcpy(cleartext_buffer, hash_buffer, length_hash);
543 memcpy(cleartext_buffer + length_hash,
544 data_payload_buffer,
545 length_data_payload);
546 OPENSSL_cleanse(data_payload_buffer, length_data_payload); /* sensitive */
547
548 /* encryption */
549 if (!(ctx = EVP_CIPHER_CTX_new())) {
550 debug_print("New ctx failed\n");
551 goto cleanup;
552 }
553
554 length_ciphertext = 0;
555 if (EVP_EncryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
556 debug_print("EVP_EncryptInit_ex failed\n");
557 goto cleanup;
558 }
559
560 ciphertext_buffer = OPENSSL_malloc(length_cleartext);
561
562 crypt_buffer_cnt = 0;
563 while(1) {
564 if (EVP_EncryptUpdate(ctx,
565 ciphertext_buffer + length_ciphertext,
566 &crypt_buffer_cnt,
567 cleartext_buffer + length_ciphertext,
568 1) != 1) { /* one byte at a time */
569 debug_print("EVP_EncryptUpdate failed\n");
570 goto cleanup;
571 }
572
573 if (crypt_buffer_cnt != 1) { /* paranoia */
574 debug_print("The sizes of enc and dec text do not correspond\n");
575 goto cleanup;
576 }
577
578 ++length_ciphertext;
579
580 if (length_ciphertext >= length_cleartext) {
581 break;
582 }
583 }
584 /* No padding required for the CFB mode */
585
586 OPENSSL_cleanse(cleartext_buffer, length_cleartext); /* sensitive */
587 length_total = SALT_SIZE + length_ciphertext;
588 total_buffer = OPENSSL_malloc(length_total);
589
590 memcpy(total_buffer, salt, SALT_SIZE);
591 memcpy(total_buffer + SALT_SIZE, ciphertext_buffer, length_ciphertext);
592
593 *encrypted = g_base64_encode(total_buffer, length_total);
594 *length_encrypted = strlen(*encrypted);
595
596 rc = RC_OK;
597
598cleanup:
599 /* key */
600 OPENSSL_cleanse(key, key_size);
601 OPENSSL_free(key);
602 /* cleartext buffer */
603 OPENSSL_cleanse(cleartext_buffer, length_cleartext);
604 OPENSSL_free(cleartext_buffer);
605 /* payload buffer */
606 OPENSSL_cleanse(data_payload_buffer, length_data_payload);
607 OPENSSL_free(data_payload_buffer);
608 /* hash */
609 OPENSSL_cleanse(hash_buffer, length_hash);
610 OPENSSL_free(hash_buffer);
611 /* padding */
612 if (length_padding > 0) {
613 OPENSSL_cleanse(padding_buffer, length_padding);
614 OPENSSL_free(padding_buffer);
615 }
616
617 OPENSSL_cleanse(str_data_size, 3); /* paranoia */
618
619 /* ciphertext buffer */
620 OPENSSL_free(ciphertext_buffer);
621
622 /* total buffer */
623 OPENSSL_free(total_buffer);
624
625 length_data_payload = 0;
626 length_padding = 0;
627
628 EVP_CIPHER_CTX_free(ctx);
629 EVP_MD_CTX_destroy(mdctx);
630
631 return rc;
632
633}
634
635
636gint decrypt_data(gchar **decrypted,
637 const gchar *data,
638 const gchar *passphrase,
639 gint length_data) {
640
641 gint rc; /* return code */
642 gint decrypt_buffer_cnt, length_ciphertext, length_decrypted;
643 guint key_size, length_hash, length_cleartext;
644 gsize length_total;
645 guchar salt[SALT_SIZE];
646 guchar *ciphertext_buffer, *total_buffer;
647 /* sensitive buffers and counters */
648 gint data_size;
649 guint length_data_payload;
650 gchar str_data_size[3];
651 guchar *data_payload_buffer, *hash_buffer, *key;
652 guchar *cleartext_buffer;
653
654 EVP_CIPHER_CTX *ctx;
655 EVP_MD_CTX *mdctx;
656
657 rc = -1;
658
659 if (length_data < 1) {
660 return -1;
661 }
662
663 total_buffer = g_base64_decode(data, &length_total);
664 length_ciphertext = length_total - SALT_SIZE;
665 ciphertext_buffer = OPENSSL_malloc(length_ciphertext);
666 OPENSSL_cleanse(ciphertext_buffer, length_ciphertext);
667
668 memcpy(salt, total_buffer, SALT_SIZE);
669 memcpy(ciphertext_buffer, total_buffer + SALT_SIZE, length_ciphertext);
670
671 key_size = EVP_CIPHER_key_length(CIPHER);
672
673 /* decryption */
674 if(!(ctx = EVP_CIPHER_CTX_new())) {
675 debug_print("New ctx failed\n");
676 goto cleanup;
677 }
678
679 key = OPENSSL_malloc(key_size);
680 OPENSSL_cleanse(key, key_size);
681 if (secure_derive_key(key,
682 key_size,
683 passphrase,
684 salt) != 0) {
685 OPENSSL_cleanse(key, key_size);
686 debug_print("Could not generate secure key\n");
687 goto cleanup;
688 }
689
690 if (EVP_DecryptInit_ex(ctx, CIPHER, NULL, key, salt) != 1) {
691 debug_print("EVP_DecryptInit_ex failed\n");
692 goto cleanup;
693 }
694 OPENSSL_cleanse(key, key_size); /* highly sensitive */
695 cleartext_buffer = OPENSSL_malloc(length_ciphertext);
696 OPENSSL_cleanse(cleartext_buffer, length_ciphertext);
697
698 length_cleartext = 0;
699 decrypt_buffer_cnt = 0;
700
701 while(1) {
702 if (EVP_DecryptUpdate(ctx,
703 cleartext_buffer + length_cleartext,
704 &decrypt_buffer_cnt,
705 ciphertext_buffer + length_cleartext,
706 1) != 1) { /* one byte at a time */
707 debug_print("EVP_EncryptUpdate failed\n");
708 goto cleanup;
709 }
710
711 if (decrypt_buffer_cnt != 1) { /* paranoia */
712 debug_print("The sizes of enc and dec text do not correspond");
713 goto cleanup;
714 }
715
716 ++length_cleartext;
717
718 if (length_cleartext >= length_ciphertext) {
719 break;
720 }
721 }
722
723
724 length_hash = EVP_MD_size(DIGEST_HASH);
725 hash_buffer = OPENSSL_malloc(length_hash);
726 length_data_payload = length_cleartext - length_hash;
727 data_payload_buffer = OPENSSL_malloc(length_data_payload);
728 OPENSSL_cleanse(data_payload_buffer, length_data_payload);
729
730 memcpy(data_payload_buffer,
731 cleartext_buffer + length_hash,
732 length_data_payload);
733
734 mdctx = EVP_MD_CTX_create();
735 EVP_DigestInit_ex(mdctx, DIGEST_HASH, NULL);
736 EVP_DigestUpdate(mdctx, data_payload_buffer, length_data_payload);
737 EVP_DigestFinal_ex(mdctx, hash_buffer, &length_hash);
738
739 if (strncmp((const gchar*) hash_buffer,
740 (const gchar*) cleartext_buffer,
741 length_hash) != 0) {
742 debug_print("Invalid hash\n");
743 rc = RC_WRONG_HASH_OR_KEY;
744 goto cleanup;
745 }
746
747 memcpy(str_data_size, cleartext_buffer + length_hash, 2);
748 data_size = atoi(str_data_size);
749
750 if (data_size < 0) {
751 length_decrypted = length_data_payload - 2;
752 } else {
753 length_decrypted = data_size;
754 }
755 *decrypted = OPENSSL_malloc(length_decrypted);
756 memcpy(*decrypted, data_payload_buffer + 2, length_decrypted);
757
758 rc = RC_OK;
759
760cleanup:
761
762 /* key */
763 OPENSSL_cleanse(key, key_size);
764 OPENSSL_free(key);
765 /* payload buffer */
766 OPENSSL_cleanse(data_payload_buffer, length_data_payload);
767 OPENSSL_free(data_payload_buffer);
768 /* hash */
769 OPENSSL_cleanse(hash_buffer, length_hash);
770 OPENSSL_free(hash_buffer);
771 /* ciphertext */
772 OPENSSL_free(ciphertext_buffer);
773 OPENSSL_free(total_buffer);
774
775 EVP_CIPHER_CTX_free(ctx);
776 EVP_MD_CTX_destroy(mdctx);
777
778 return rc;
779
780}
781
405#endif /* USE_SSL */ 782#endif /* USE_SSL */
diff --git a/libsylph/ssl.h b/libsylph/ssl.h
index a9f690d..94ab4b7 100644
--- a/libsylph/ssl.h
+++ b/libsylph/ssl.h
@@ -32,9 +32,15 @@
32#include <openssl/pem.h> 32#include <openssl/pem.h>
33#include <openssl/ssl.h> 33#include <openssl/ssl.h>
34#include <openssl/err.h> 34#include <openssl/err.h>
35#include <openssl/evp.h>
36#include <openssl/rand.h>
35 37
36#include "socket.h" 38#include "socket.h"
37 39
40#define RC_OK 0
41#define RC_ERROR -1
42#define RC_WRONG_HASH_OR_KEY 1
43
38typedef enum { 44typedef enum {
39 SSL_METHOD_SSLv23, 45 SSL_METHOD_SSLv23,
40 SSL_METHOD_TLSv1 46 SSL_METHOD_TLSv1
@@ -60,6 +66,20 @@ void ssl_done_socket (SockInfo *sockinfo);
60 66
61void ssl_set_verify_func (SSLVerifyFunc func); 67void ssl_set_verify_func (SSLVerifyFunc func);
62 68
69/* master password related code */
70gint encrypt_data(gchar **encrypted,
71 gint *length_encrypted,
72 const gchar *data,
73 const gchar *passphrase,
74 gint length_data,
75 guint min_data_length,
76 gboolean rnd_salt);
77
78gint decrypt_data(gchar **decrypted,
79 const gchar *data,
80 const gchar *passphrase,
81 gint length_data);
82/* ---------------------------- */
63#endif /* USE_SSL */ 83#endif /* USE_SSL */
64 84
65#endif /* __SSL_H__ */ 85#endif /* __SSL_H__ */
diff --git a/src/send_message.c b/src/send_message.c
index a8c2c7a..695246a 100644
--- a/src/send_message.c
+++ b/src/send_message.c
@@ -648,13 +648,23 @@ static gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp)
648 648
649 if (ac_prefs->smtp_userid) { 649 if (ac_prefs->smtp_userid) {
650 smtp_session->user = g_strdup(ac_prefs->smtp_userid); 650 smtp_session->user = g_strdup(ac_prefs->smtp_userid);
651 if (ac_prefs->smtp_passwd) 651 if (ac_prefs->smtp_passwd) {
652 smtp_session->pass = 652 if(ac_prefs->master_password != NULL) {
653 g_strdup(ac_prefs->smtp_passwd); 653 if (decrypt_data(&(smtp_session->pass),
654 else if (ac_prefs->tmp_smtp_pass) 654 ac_prefs->smtp_passwd,
655 ac_prefs->master_password,
656 strlen(ac_prefs->smtp_passwd)) != RC_OK) {
657 session_destroy(session);
658 return -1;
659 }
660 } else {
661 smtp_session->pass =
662 g_strdup(ac_prefs->smtp_passwd);
663 }
664 } else if (ac_prefs->tmp_smtp_pass) {
655 smtp_session->pass = 665 smtp_session->pass =
656 g_strdup(ac_prefs->tmp_smtp_pass); 666 g_strdup(ac_prefs->tmp_smtp_pass);
657 else { 667 } else {
658 smtp_session->pass = 668 smtp_session->pass =
659 input_query_password 669 input_query_password
660 (ac_prefs->smtp_server, 670 (ac_prefs->smtp_server,