OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "bin/secure_socket.h" | 5 #include "bin/secure_socket.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <sys/stat.h> | 9 #include <sys/stat.h> |
10 #include <stdio.h> | 10 #include <stdio.h> |
(...skipping 506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 Dart_Handle object_; | 517 Dart_Handle object_; |
518 uint8_t* bytes_; | 518 uint8_t* bytes_; |
519 intptr_t bytes_len_; | 519 intptr_t bytes_len_; |
520 BIO* bio_; | 520 BIO* bio_; |
521 bool is_typed_data_; | 521 bool is_typed_data_; |
522 | 522 |
523 DISALLOW_ALLOCATION(); | 523 DISALLOW_ALLOCATION(); |
524 DISALLOW_COPY_AND_ASSIGN(ScopedMemBIO); | 524 DISALLOW_COPY_AND_ASSIGN(ScopedMemBIO); |
525 }; | 525 }; |
526 | 526 |
527 | |
528 template<typename T, void (*free_func)(T*)> | 527 template<typename T, void (*free_func)(T*)> |
529 class ScopedSSLType { | 528 class ScopedSSLType { |
530 public: | 529 public: |
531 explicit ScopedSSLType(T* obj) : obj_(obj) {} | 530 explicit ScopedSSLType(T* obj) : obj_(obj) {} |
532 | 531 |
533 ~ScopedSSLType() { | 532 ~ScopedSSLType() { |
534 if (obj_ != NULL) { | 533 if (obj_ != NULL) { |
535 free_func(obj_); | 534 free_func(obj_); |
536 } | 535 } |
537 } | 536 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
575 | 574 |
576 private: | 575 private: |
577 T* obj_; | 576 T* obj_; |
578 | 577 |
579 DISALLOW_ALLOCATION(); | 578 DISALLOW_ALLOCATION(); |
580 DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType); | 579 DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType); |
581 }; | 580 }; |
582 | 581 |
583 typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12; | 582 typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12; |
584 typedef ScopedSSLType<X509, X509_free> ScopedX509; | 583 typedef ScopedSSLType<X509, X509_free> ScopedX509; |
| 584 typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack; |
585 | 585 |
586 typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack; | 586 static bool NoPEMStartLine() { |
587 typedef ScopedSSLStackType<STACK_OF(X509_NAME), X509_NAME, X509_NAME_free> | |
588 ScopedX509NAMEStack; | |
589 | |
590 | |
591 // We try reading data as PKCS12 only if reading as PEM was unsuccessful and | |
592 // if there is no indication that the data is malformed PEM. We assume the data | |
593 // is malformed PEM if it contains the start line, i.e. a line with ----- BEGIN. | |
594 static bool TryPKCS12(bool pem_success) { | |
595 uint32_t last_error = ERR_peek_last_error(); | 587 uint32_t last_error = ERR_peek_last_error(); |
596 return !pem_success && | 588 return (ERR_GET_LIB(last_error) == ERR_LIB_PEM) && |
597 (ERR_GET_LIB(last_error) == ERR_LIB_PEM) && | 589 (ERR_GET_REASON(last_error) == PEM_R_NO_START_LINE); |
598 (ERR_GET_REASON(last_error) == PEM_R_NO_START_LINE); | |
599 } | 590 } |
600 | 591 |
601 | 592 |
602 static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) { | 593 static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) { |
603 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); | 594 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
604 if (p12.get() == NULL) { | 595 if (p12.get() == NULL) { |
605 return NULL; | 596 return NULL; |
606 } | 597 } |
607 | 598 |
608 EVP_PKEY* key = NULL; | 599 EVP_PKEY* key = NULL; |
609 X509 *cert = NULL; | 600 X509 *cert = NULL; |
610 STACK_OF(X509) *ca_certs = NULL; | 601 STACK_OF(X509) *ca_certs = NULL; |
611 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); | 602 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); |
612 if (status == 0) { | 603 if (status == 0) { |
613 return NULL; | 604 return NULL; |
614 } | 605 } |
615 | 606 |
616 // We only care about the private key. | 607 // We only care about the private key. |
617 ScopedX509 delete_cert(cert); | 608 ScopedX509 delete_cert(cert); |
618 ScopedX509Stack delete_ca_certs(ca_certs); | 609 ScopedX509Stack delete_ca_certs(ca_certs); |
619 return key; | 610 return key; |
620 } | 611 } |
621 | 612 |
622 | 613 |
623 static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) { | 614 static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) { |
624 EVP_PKEY *key = PEM_read_bio_PrivateKey( | 615 EVP_PKEY *key = PEM_read_bio_PrivateKey( |
625 bio, NULL, PasswordCallback, const_cast<char*>(password)); | 616 bio, NULL, PasswordCallback, const_cast<char*>(password)); |
626 if (TryPKCS12(key != NULL)) { | 617 if (key == NULL) { |
627 // Reset the bio, and clear the error from trying to read as PEM. | 618 // We try reading data as PKCS12 only if reading as PEM was unsuccessful and |
628 ERR_clear_error(); | 619 // if there is no indication that the data is malformed PEM. We assume the |
629 BIO_reset(bio); | 620 // data is malformed PEM if it contains the start line, i.e. a line |
| 621 // with ----- BEGIN. |
| 622 if (NoPEMStartLine()) { |
| 623 // Reset the bio, and clear the error from trying to read as PEM. |
| 624 ERR_clear_error(); |
| 625 BIO_reset(bio); |
630 | 626 |
631 // Try to decode as PKCS12 | 627 // Try to decode as PKCS12. |
632 key = GetPrivateKeyPKCS12(bio, password); | 628 key = GetPrivateKeyPKCS12(bio, password); |
| 629 } |
633 } | 630 } |
634 return key; | 631 return key; |
635 } | 632 } |
636 | 633 |
637 | 634 |
638 static const char* GetPasswordArgument(Dart_NativeArguments args, | 635 static const char* GetPasswordArgument(Dart_NativeArguments args, |
639 intptr_t index) { | 636 intptr_t index) { |
640 Dart_Handle password_object = | 637 Dart_Handle password_object = |
641 ThrowIfError(Dart_GetNativeArgument(args, index)); | 638 ThrowIfError(Dart_GetNativeArgument(args, index)); |
642 const char* password = NULL; | 639 const char* password = NULL; |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
718 int status = 0; | 715 int status = 0; |
719 X509* cert = NULL; | 716 X509* cert = NULL; |
720 while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { | 717 while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
721 status = X509_STORE_add_cert(store, cert); | 718 status = X509_STORE_add_cert(store, cert); |
722 if (status == 0) { | 719 if (status == 0) { |
723 X509_free(cert); | 720 X509_free(cert); |
724 return status; | 721 return status; |
725 } | 722 } |
726 } | 723 } |
727 | 724 |
728 // If bio does not contain PEM data, the first call to PEM_read_bio_X509 will | 725 // If no PEM start line is found, it means that we read to the end of the |
729 // return NULL, and the while-loop will exit while status is still 0. | 726 // file, or that the file isn't PEM. In the first case, status will be |
730 uint32_t err = ERR_peek_last_error(); | 727 // non-zero indicating success. In the second case, status will be 0, |
731 if ((ERR_GET_LIB(err) != ERR_LIB_PEM) || | 728 // indicating that we should try to read as PKCS12. If there is some other |
732 (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)) { | 729 // error, we return it up to the caller. |
733 // If bio contains data that is trying to be PEM but is malformed, then | 730 return NoPEMStartLine() ? status : 0; |
734 // this case will be triggered. | |
735 status = 0; | |
736 } | |
737 | |
738 return status; | |
739 } | 731 } |
740 | 732 |
741 | 733 |
742 static int SetTrustedCertificatesBytes(SSL_CTX* context, | 734 static int SetTrustedCertificatesBytes(SSL_CTX* context, |
743 BIO* bio, | 735 BIO* bio, |
744 const char* password) { | 736 const char* password) { |
745 int status = SetTrustedCertificatesBytesPEM(context, bio); | 737 int status = SetTrustedCertificatesBytesPEM(context, bio); |
746 if (TryPKCS12(status != 0)) { | 738 if (status == 0) { |
747 ERR_clear_error(); | 739 if (NoPEMStartLine()) { |
748 BIO_reset(bio); | 740 ERR_clear_error(); |
749 status = SetTrustedCertificatesBytesPKCS12(context, bio, password); | 741 BIO_reset(bio); |
750 } else if (status != 0) { | 742 status = SetTrustedCertificatesBytesPKCS12(context, bio, password); |
| 743 } |
| 744 } else { |
751 // The PEM file was successfully parsed. | 745 // The PEM file was successfully parsed. |
752 ERR_clear_error(); | 746 ERR_clear_error(); |
753 } | 747 } |
754 return status; | 748 return status; |
755 } | 749 } |
756 | 750 |
757 | 751 |
758 void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( | 752 void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( |
759 Dart_NativeArguments args) { | 753 Dart_NativeArguments args) { |
760 SSL_CTX* context = GetSecurityContext(args); | 754 SSL_CTX* context = GetSecurityContext(args); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
853 status = SSL_CTX_add0_chain_cert(context, ca); | 847 status = SSL_CTX_add0_chain_cert(context, ca); |
854 if (status == 0) { | 848 if (status == 0) { |
855 X509_free(ca); | 849 X509_free(ca); |
856 return status; | 850 return status; |
857 } | 851 } |
858 // Note that we must not free `ca` if it was successfully added to the | 852 // Note that we must not free `ca` if it was successfully added to the |
859 // chain. We must free the main certificate x509, though since its reference | 853 // chain. We must free the main certificate x509, though since its reference |
860 // count is increased by SSL_CTX_use_certificate. | 854 // count is increased by SSL_CTX_use_certificate. |
861 } | 855 } |
862 | 856 |
863 // If bio does not contain PEM data, the first call to PEM_read_bio_X509 will | 857 return NoPEMStartLine() ? status : 0; |
864 // return NULL, and the while-loop will exit while status is still 0. | |
865 uint32_t err = ERR_peek_last_error(); | |
866 if ((ERR_GET_LIB(err) != ERR_LIB_PEM) || | |
867 (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)) { | |
868 // If bio contains data that is trying to be PEM but is malformed, then | |
869 // this case will be triggered. | |
870 status = 0; | |
871 } | |
872 | |
873 return status; | |
874 } | 858 } |
875 | 859 |
876 | 860 |
877 static int UseChainBytes(SSL_CTX* context, BIO* bio, const char* password) { | 861 static int UseChainBytes(SSL_CTX* context, BIO* bio, const char* password) { |
878 int status = UseChainBytesPEM(context, bio); | 862 int status = UseChainBytesPEM(context, bio); |
879 if (TryPKCS12(status != 0)) { | 863 if (status == 0) { |
880 ERR_clear_error(); | 864 if (NoPEMStartLine()) { |
881 BIO_reset(bio); | 865 ERR_clear_error(); |
882 status = UseChainBytesPKCS12(context, bio, password); | 866 BIO_reset(bio); |
883 } else if (status != 0) { | 867 status = UseChainBytesPKCS12(context, bio, password); |
| 868 } |
| 869 } else { |
884 // The PEM file was successfully read. | 870 // The PEM file was successfully read. |
885 ERR_clear_error(); | 871 ERR_clear_error(); |
886 } | 872 } |
887 return status; | 873 return status; |
888 } | 874 } |
889 | 875 |
890 | 876 |
891 void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( | 877 void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( |
892 Dart_NativeArguments args) { | 878 Dart_NativeArguments args) { |
893 SSL_CTX* context = GetSecurityContext(args); | 879 SSL_CTX* context = GetSecurityContext(args); |
894 const char* password = GetPasswordArgument(args, 2); | 880 const char* password = GetPasswordArgument(args, 2); |
895 int status; | 881 int status; |
896 { | 882 { |
897 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); | 883 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
898 status = UseChainBytes(context, bio.bio(), password); | 884 status = UseChainBytes(context, bio.bio(), password); |
899 } | 885 } |
900 CheckStatus(status, | 886 CheckStatus(status, |
901 "TlsException", | 887 "TlsException", |
902 "Failure in useCertificateChainBytes"); | 888 "Failure in useCertificateChainBytes"); |
903 } | 889 } |
904 | 890 |
905 | 891 |
906 static STACK_OF(X509_NAME)* GetCertificateNamesPKCS12(BIO* bio, | 892 static int SetClientAuthoritiesPKCS12(SSL_CTX* context, |
907 const char* password) { | 893 BIO* bio, |
| 894 const char* password) { |
908 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); | 895 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
909 if (p12.get() == NULL) { | 896 if (p12.get() == NULL) { |
910 return NULL; | 897 return 0; |
911 } | |
912 | |
913 ScopedX509NAMEStack result(sk_X509_NAME_new_null()); | |
914 if (result.get() == NULL) { | |
915 return NULL; | |
916 } | 898 } |
917 | 899 |
918 EVP_PKEY* key = NULL; | 900 EVP_PKEY* key = NULL; |
919 X509 *cert = NULL; | 901 X509 *cert = NULL; |
920 STACK_OF(X509) *ca_certs = NULL; | 902 STACK_OF(X509) *ca_certs = NULL; |
921 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); | 903 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); |
922 if (status == 0) { | 904 if (status == 0) { |
923 return NULL; | 905 return status; |
924 } | 906 } |
925 | 907 |
926 ScopedX509 x509(cert); | 908 ScopedX509Stack cert_stack(ca_certs); |
927 ScopedX509Stack certs(ca_certs); | 909 status = SSL_CTX_add_client_CA(context, cert); |
928 X509_NAME* x509_name = X509_get_subject_name(x509.get()); | 910 if (status == 0) { |
929 if (x509_name == NULL) { | 911 X509_free(cert); |
930 return NULL; | 912 return status; |
931 } | 913 } |
932 | 914 |
933 x509_name = X509_NAME_dup(x509_name); | 915 X509* ca; |
934 if (x509_name == NULL) { | 916 while ((ca = sk_X509_shift(cert_stack.get())) != NULL) { |
935 return NULL; | 917 status = SSL_CTX_add_client_CA(context, ca); |
| 918 X509_free(ca); // The name has been extracted. |
| 919 if (status == 0) { |
| 920 return status; |
| 921 } |
936 } | 922 } |
937 | 923 |
938 sk_X509_NAME_push(result.get(), x509_name); | 924 return status; |
939 | |
940 while (true) { | |
941 ScopedX509 ca(sk_X509_shift(certs.get())); | |
942 if (ca.get() == NULL) { | |
943 break; | |
944 } | |
945 | |
946 X509_NAME* x509_name = X509_get_subject_name(ca.get()); | |
947 if (x509_name == NULL) { | |
948 return NULL; | |
949 } | |
950 | |
951 x509_name = X509_NAME_dup(x509_name); | |
952 if (x509_name == NULL) { | |
953 return NULL; | |
954 } | |
955 | |
956 sk_X509_NAME_push(result.get(), x509_name); | |
957 } | |
958 | |
959 return result.release(); | |
960 } | 925 } |
961 | 926 |
962 | 927 |
963 static STACK_OF(X509_NAME)* GetCertificateNamesPEM(BIO* bio) { | 928 static int SetClientAuthoritiesPEM(SSL_CTX* context, BIO* bio) { |
964 ScopedX509NAMEStack result(sk_X509_NAME_new_null()); | 929 int status = 0; |
965 if (result.get() == NULL) { | 930 X509* cert = NULL; |
966 return NULL; | 931 while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
| 932 status = SSL_CTX_add_client_CA(context, cert); |
| 933 X509_free(cert); // The name has been extracted. |
| 934 if (status == 0) { |
| 935 return status; |
| 936 } |
967 } | 937 } |
968 | 938 return NoPEMStartLine() ? status : 0; |
969 while (true) { | |
970 ScopedX509 x509(PEM_read_bio_X509(bio, NULL, NULL, NULL)); | |
971 if (x509.get() == NULL) { | |
972 break; | |
973 } | |
974 | |
975 X509_NAME* x509_name = X509_get_subject_name(x509.get()); | |
976 if (x509_name == NULL) { | |
977 return NULL; | |
978 } | |
979 | |
980 // Duplicate the name to put it on the stack. | |
981 x509_name = X509_NAME_dup(x509_name); | |
982 if (x509_name == NULL) { | |
983 return NULL; | |
984 } | |
985 sk_X509_NAME_push(result.get(), x509_name); | |
986 } | |
987 | |
988 if (sk_X509_NAME_num(result.get()) == 0) { | |
989 // The data was not PEM. | |
990 return NULL; | |
991 } | |
992 | |
993 uint32_t err = ERR_peek_last_error(); | |
994 if ((ERR_GET_LIB(err) != ERR_LIB_PEM) || | |
995 (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)) { | |
996 // The data was trying to be PEM, but was malformed. | |
997 return NULL; | |
998 } | |
999 | |
1000 return result.release(); | |
1001 } | 939 } |
1002 | 940 |
1003 | 941 |
1004 static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio, | 942 static int SetClientAuthorities(SSL_CTX* context, |
1005 const char* password) { | 943 BIO* bio, |
1006 STACK_OF(X509_NAME)* result = GetCertificateNamesPEM(bio); | 944 const char* password) { |
1007 if (TryPKCS12(result != NULL)) { | 945 int status = SetClientAuthoritiesPEM(context, bio); |
1008 ERR_clear_error(); | 946 if (status == 0) { |
1009 BIO_reset(bio); | 947 if (NoPEMStartLine()) { |
1010 result = GetCertificateNamesPKCS12(bio, password); | 948 ERR_clear_error(); |
1011 } else if (result != NULL) { | 949 BIO_reset(bio); |
| 950 status = SetClientAuthoritiesPKCS12(context, bio, password); |
| 951 } |
| 952 } else { |
1012 // The PEM file was successfully parsed. | 953 // The PEM file was successfully parsed. |
1013 ERR_clear_error(); | 954 ERR_clear_error(); |
1014 } | 955 } |
1015 return result; | 956 return status; |
1016 } | 957 } |
1017 | 958 |
1018 | 959 |
1019 void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( | 960 void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( |
1020 Dart_NativeArguments args) { | 961 Dart_NativeArguments args) { |
1021 SSL_CTX* context = GetSecurityContext(args); | 962 SSL_CTX* context = GetSecurityContext(args); |
1022 const char* password = GetPasswordArgument(args, 2); | 963 const char* password = GetPasswordArgument(args, 2); |
1023 STACK_OF(X509_NAME)* certificate_names; | |
1024 | 964 |
| 965 int status; |
1025 { | 966 { |
1026 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); | 967 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
1027 certificate_names = GetCertificateNames(bio.bio(), password); | 968 status = SetClientAuthorities(context, bio.bio(), password); |
1028 } | 969 } |
1029 | 970 |
1030 if (certificate_names != NULL) { | 971 CheckStatus(status, |
1031 SSL_CTX_set_client_CA_list(context, certificate_names); | 972 "TlsException", |
1032 } else { | 973 "Failure in setClientAuthoritiesBytes"); |
1033 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
1034 "Could not load certificate names from file in SetClientAuthorities")); | |
1035 } | |
1036 } | 974 } |
1037 | 975 |
1038 | 976 |
1039 void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)( | 977 void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)( |
1040 Dart_NativeArguments args) { | 978 Dart_NativeArguments args) { |
1041 SSL_CTX* context = GetSecurityContext(args); | 979 SSL_CTX* context = GetSecurityContext(args); |
1042 Dart_Handle protocols_handle = | 980 Dart_Handle protocols_handle = |
1043 ThrowIfError(Dart_GetNativeArgument(args, 1)); | 981 ThrowIfError(Dart_GetNativeArgument(args, 1)); |
1044 Dart_Handle is_server_handle = | 982 Dart_Handle is_server_handle = |
1045 ThrowIfError(Dart_GetNativeArgument(args, 2)); | 983 ThrowIfError(Dart_GetNativeArgument(args, 2)); |
(...skipping 667 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1713 } else { | 1651 } else { |
1714 if (SSL_LOG_DATA) Log::Print( | 1652 if (SSL_LOG_DATA) Log::Print( |
1715 "WriteEncrypted BIO_read wrote %d bytes\n", bytes_processed); | 1653 "WriteEncrypted BIO_read wrote %d bytes\n", bytes_processed); |
1716 } | 1654 } |
1717 } | 1655 } |
1718 return bytes_processed; | 1656 return bytes_processed; |
1719 } | 1657 } |
1720 | 1658 |
1721 } // namespace bin | 1659 } // namespace bin |
1722 } // namespace dart | 1660 } // namespace dart |
OLD | NEW |