mutt stable branch with some hacks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Rework OpenSSL certificate verification to support alternative chains. (closes #3903)

The way Mutt currently verifies SSL certificates using OpenSSL does
not support alternative chains, which may cause confusion when some
popular mail providers (e.g. Gmail) are used with specific sets of
trusted CA certificates.

Replace the "manual" verification done by mutt in
check_certificate_by_signer() with SSL_set_verify() using a callback.
OpenSSL then does the certificate verification, including properly
looking at alternative chains. The callback still provides the
opportunity to override using ~/.mutt_certificates or an interactive
prompt.

+91 -131
+91 -131
mutt_ssl.c
··· 54 54 #define HAVE_ENTROPY() (!access(DEVRANDOM, R_OK) || entropy_byte_count >= 16) 55 55 #endif 56 56 57 + /* index for storing hostname as application specific data in SSL structure */ 58 + static int HostExDataIndex = -1; 57 59 /* keep a handle on accepted certificates in case we want to 58 60 * open up another connection to the same server in this session */ 59 61 static STACK_OF(X509) *SslSessionCerts = NULL; ··· 78 80 static void ssl_err (sslsockdata *data, int err); 79 81 static void ssl_dprint_err_stack (void); 80 82 static int ssl_cache_trusted_cert (X509 *cert); 81 - static int ssl_check_certificate (CONNECTION *conn, sslsockdata * data); 83 + static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx); 82 84 static int interactive_check_cert (X509 *cert, int idx, int len); 83 85 static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn); 84 86 static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata); ··· 132 134 dprint(1, (debugfile, "mutt_ssl_starttls: Error setting options to %ld\n", ssl_options)); 133 135 goto bail_ctx; 134 136 } 137 + 138 + if (option (OPTSSLSYSTEMCERTS)) 139 + { 140 + if (! SSL_CTX_set_default_verify_paths (ssldata->ctx)) 141 + { 142 + dprint (1, (debugfile, "mutt_ssl_starttls: Error setting default verify paths\n")); 143 + goto bail_ctx; 144 + } 145 + } 146 + 147 + if (SslCertFile && ! SSL_CTX_load_verify_locations (ssldata->ctx, SslCertFile, NULL)) 148 + dprint (1, (debugfile, "mutt_ssl_starttls: Error loading trusted certificates\n")); 135 149 136 150 ssl_get_client_cert(ssldata, conn); 137 151 ··· 370 384 SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3); 371 385 } 372 386 387 + if (option (OPTSSLSYSTEMCERTS)) 388 + { 389 + if (! SSL_CTX_set_default_verify_paths (data->ctx)) 390 + { 391 + dprint (1, (debugfile, "ssl_socket_open: Error setting default verify paths\n")); 392 + mutt_socket_close (conn); 393 + return -1; 394 + } 395 + } 396 + 397 + if (SslCertFile && ! SSL_CTX_load_verify_locations (data->ctx, SslCertFile, NULL)) 398 + dprint (1, (debugfile, "ssl_socket_open: Error loading trusted certificates\n")); 399 + 373 400 ssl_get_client_cert(data, conn); 374 401 375 402 if (SslCiphers) { ··· 400 427 int err; 401 428 const char* errmsg; 402 429 430 + if ((HostExDataIndex = SSL_get_ex_new_index (0, "host", NULL, NULL, NULL)) == -1) 431 + { 432 + dprint (1, (debugfile, "failed to get index for application specific data\n")); 433 + return -1; 434 + } 435 + 436 + if (! SSL_set_ex_data (ssldata->ssl, HostExDataIndex, conn->account.host)) 437 + { 438 + dprint (1, (debugfile, "failed to save hostname in SSL structure\n")); 439 + return -1; 440 + } 441 + 442 + SSL_set_verify (ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback); 403 443 SSL_set_mode (ssldata->ssl, SSL_MODE_AUTO_RETRY); 404 444 405 445 if ((err = SSL_connect (ssldata->ssl)) != 1) ··· 421 461 422 462 return -1; 423 463 } 424 - 425 - ssldata->cert = SSL_get_peer_certificate (ssldata->ssl); 426 - if (!ssldata->cert) 427 - { 428 - mutt_error (_("Unable to get certificate from peer")); 429 - mutt_sleep (1); 430 - return -1; 431 - } 432 - 433 - if (!ssl_check_certificate (conn, ssldata)) 434 - return -1; 435 464 436 465 /* L10N: 437 466 %1$s is version (e.g. "TLSv1.2") ··· 608 637 return buf; 609 638 } 610 639 611 - static int check_certificate_by_signer (X509 *peercert) 612 - { 613 - X509_STORE_CTX *xsc; 614 - X509_STORE *ctx; 615 - int pass = 0, i; 616 - 617 - ctx = X509_STORE_new (); 618 - if (ctx == NULL) return 0; 619 - 620 - if (option (OPTSSLSYSTEMCERTS)) 621 - { 622 - if (X509_STORE_set_default_paths (ctx)) 623 - pass++; 624 - else 625 - dprint (2, (debugfile, "X509_STORE_set_default_paths failed\n")); 626 - } 627 - 628 - if (X509_STORE_load_locations (ctx, SslCertFile, NULL)) 629 - pass++; 630 - else 631 - dprint (2, (debugfile, "X509_STORE_load_locations failed\n")); 632 - 633 - for (i = 0; i < sk_X509_num (SslSessionCerts); i++) 634 - pass += (X509_STORE_add_cert (ctx, sk_X509_value (SslSessionCerts, i)) != 0); 635 - 636 - if (pass == 0) 637 - { 638 - /* nothing to do */ 639 - X509_STORE_free (ctx); 640 - return 0; 641 - } 642 - 643 - xsc = X509_STORE_CTX_new(); 644 - if (xsc == NULL) return 0; 645 - X509_STORE_CTX_init (xsc, ctx, peercert, SslSessionCerts); 646 - 647 - pass = (X509_verify_cert (xsc) > 0); 648 - #ifdef DEBUG 649 - if (! pass) 650 - { 651 - char buf[SHORT_STRING]; 652 - int err; 653 - 654 - err = X509_STORE_CTX_get_error (xsc); 655 - snprintf (buf, sizeof (buf), "%s (%d)", 656 - X509_verify_cert_error_string(err), err); 657 - dprint (2, (debugfile, "X509_verify_cert: %s\n", buf)); 658 - } 659 - #endif 660 - X509_STORE_CTX_free (xsc); 661 - X509_STORE_free (ctx); 662 - 663 - return pass; 664 - } 665 - 666 640 static int compare_certificates (X509 *cert, X509 *peercert, 667 641 unsigned char *peermd, unsigned int peermdlen) 668 642 { ··· 908 882 return (sk_X509_push (SslSessionCerts, X509_dup(c))); 909 883 } 910 884 911 - /* check whether cert is preauthorized. If host is not null, verify that 912 - * it matches the certificate. 913 - * Return > 0: authorized, < 0: problems, 0: unknown validity */ 914 - static int ssl_check_preauth (X509 *cert, const char* host) 885 + /* certificate verification callback, called for each certificate in the chain 886 + * sent by the peer, starting from the root; returning 1 means that the given 887 + * certificate is trusted, returning 0 immediately aborts the SSL connection */ 888 + static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx) 915 889 { 916 - char buf[SHORT_STRING]; 890 + char buf[STRING]; 891 + const char *host; 892 + int len, pos; 893 + X509 *cert; 894 + SSL *ssl; 895 + 896 + if (! (ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ()))) 897 + { 898 + dprint (1, (debugfile, "ssl_verify_callback: failed to retrieve SSL structure from X509_STORE_CTX\n")); 899 + return 0; 900 + } 901 + if (! (host = SSL_get_ex_data (ssl, HostExDataIndex))) 902 + { 903 + dprint (1, (debugfile, "ssl_verify_callback: failed to retrieve hostname from SSL structure\n")); 904 + return 0; 905 + } 906 + 907 + cert = X509_STORE_CTX_get_current_cert (ctx); 908 + pos = X509_STORE_CTX_get_error_depth (ctx); 909 + len = sk_X509_num (X509_STORE_CTX_get_chain (ctx)); 910 + 911 + dprint (1, (debugfile, "ssl_verify_callback: checking cert chain entry %s (preverify: %d)\n", 912 + X509_NAME_oneline (X509_get_subject_name (cert), 913 + buf, sizeof (buf)), preverify_ok)); 917 914 918 915 /* check session cache first */ 919 916 if (check_certificate_cache (cert)) 920 917 { 921 - dprint (2, (debugfile, "ssl_check_preauth: using cached certificate\n")); 918 + dprint (2, (debugfile, "ssl_verify_callback: using cached certificate\n")); 922 919 return 1; 923 920 } 924 921 922 + /* check hostname only for the leaf certificate */ 925 923 buf[0] = 0; 926 - if (host && option (OPTSSLVERIFYHOST) != MUTT_NO) 924 + if (pos == 0 && option (OPTSSLVERIFYHOST) != MUTT_NO) 927 925 { 928 926 if (!check_host (cert, host, buf, sizeof (buf))) 929 927 { 930 928 mutt_error (_("Certificate host check failed: %s"), buf); 931 929 mutt_sleep (2); 932 - return -1; 930 + return interactive_check_cert (cert, pos, len); 933 931 } 934 - dprint (2, (debugfile, "ssl_check_preauth: hostname check passed\n")); 932 + dprint (2, (debugfile, "ssl_verify_callback: hostname check passed\n")); 935 933 } 936 934 937 - if (check_certificate_by_signer (cert)) 935 + if (!preverify_ok) 938 936 { 939 - dprint (2, (debugfile, "ssl_check_preauth: signer check passed\n")); 940 - return 1; 941 - } 937 + /* automatic check from user's database */ 938 + if (SslCertFile && check_certificate_by_digest (cert)) 939 + { 940 + dprint (2, (debugfile, "ssl_verify_callback: digest check passed\n")); 941 + return 1; 942 + } 942 943 943 - /* automatic check from user's database */ 944 - if (SslCertFile && check_certificate_by_digest (cert)) 945 - { 946 - dprint (2, (debugfile, "ssl_check_preauth: digest check passed\n")); 947 - return 1; 948 - } 949 - 950 - return 0; 951 - } 952 - 953 - static int ssl_check_certificate (CONNECTION *conn, sslsockdata *data) 954 - { 955 - int i, preauthrc, chain_len; 956 - STACK_OF(X509) *chain; 957 - X509 *cert; 958 944 #ifdef DEBUG 959 - char buf[STRING]; 960 - 961 - /* Note that X509_NAME_oneline will null-terminate buf, even when it 962 - * has to truncate the data. */ 963 - dprint (1, (debugfile, "ssl_check_certificate: checking cert %s\n", 964 - X509_NAME_oneline (X509_get_subject_name (data->cert), 965 - buf, sizeof (buf)))); 945 + /* log verification error */ 946 + { 947 + int err = X509_STORE_CTX_get_error (ctx); 948 + snprintf (buf, sizeof (buf), "%s (%d)", 949 + X509_verify_cert_error_string (err), err); 950 + dprint (2, (debugfile, "X509_verify_cert: %s\n", buf)); 951 + } 966 952 #endif 967 953 968 - if ((preauthrc = ssl_check_preauth (data->cert, conn->account.host)) > 0) 969 - return preauthrc; 970 - 971 - chain = SSL_get_peer_cert_chain (data->ssl); 972 - chain_len = sk_X509_num (chain); 973 - /* negative preauthrc means the certificate won't be accepted without 974 - * manual override. */ 975 - if (preauthrc < 0 || !chain || (chain_len <= 1)) 976 - return interactive_check_cert (data->cert, 0, 0); 977 - 978 - /* check the chain from root to peer. */ 979 - for (i = chain_len-1; i >= 0; i--) 980 - { 981 - cert = sk_X509_value (chain, i); 982 - 983 - dprint (1, (debugfile, "ssl_check_certificate: checking cert chain entry %s\n", 984 - X509_NAME_oneline (X509_get_subject_name (cert), 985 - buf, sizeof (buf)))); 986 - 987 - /* if the certificate validates or is manually accepted, then add it to 988 - * the trusted set and recheck the peer certificate */ 989 - if (ssl_check_preauth (cert, NULL) 990 - || interactive_check_cert (cert, i, chain_len)) 991 - { 992 - ssl_cache_trusted_cert (cert); 993 - if (ssl_check_preauth (data->cert, conn->account.host)) 994 - return 1; 995 - } 954 + /* prompt user */ 955 + return interactive_check_cert (cert, pos, len); 996 956 } 997 957 998 - return 0; 958 + return 1; 999 959 } 1000 960 1001 961 static int interactive_check_cert (X509 *cert, int idx, int len)