Index: runtime/bin/secure_socket.cc |
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc |
index fa1f7f117c8a76d849f6aaad438faf90806d8b99..2e0bb880c6742535f6b3afab345966bcc09de1e0 100644 |
--- a/runtime/bin/secure_socket.cc |
+++ b/runtime/bin/secure_socket.cc |
@@ -12,6 +12,7 @@ |
#include <openssl/bio.h> |
#include <openssl/err.h> |
+#include <openssl/pkcs12.h> |
#include <openssl/safestack.h> |
#include <openssl/ssl.h> |
#include <openssl/tls1.h> |
@@ -381,11 +382,11 @@ void CheckStatus(int status, |
// BIO is allocated. Leaving the scope cleans up the BIO and the buffer that |
// was used to create it. |
// |
-// Do not make Dart_ API calls while in a MemBIOScope. |
-// Do not call Dart_PropagateError while in a MemBIOScope. |
-class MemBIOScope { |
+// Do not make Dart_ API calls while in a ScopedMemBIO. |
+// Do not call Dart_PropagateError while in a ScopedMemBIO. |
+class ScopedMemBIO { |
public: |
- explicit MemBIOScope(Dart_Handle object) { |
+ explicit ScopedMemBIO(Dart_Handle object) { |
if (!Dart_IsTypedData(object) && !Dart_IsList(object)) { |
Dart_ThrowException(DartUtils::NewDartArgumentError( |
"Argument is not a List<int>")); |
@@ -418,7 +419,7 @@ class MemBIOScope { |
is_typed_data_ = is_typed_data; |
} |
- ~MemBIOScope() { |
+ ~ScopedMemBIO() { |
ASSERT(bio_ != NULL); |
if (is_typed_data_) { |
BIO_free(bio_); |
@@ -441,10 +442,113 @@ class MemBIOScope { |
bool is_typed_data_; |
DISALLOW_ALLOCATION(); |
- DISALLOW_COPY_AND_ASSIGN(MemBIOScope); |
+ DISALLOW_COPY_AND_ASSIGN(ScopedMemBIO); |
}; |
+template<typename T, void (*free_func)(T*)> |
+class ScopedSSLType { |
+ public: |
+ explicit ScopedSSLType(T* obj) : obj_(obj) {} |
+ |
+ ~ScopedSSLType() { |
+ if (obj_ != NULL) { |
+ free_func(obj_); |
+ } |
+ } |
+ |
+ T* get() { return obj_; } |
+ const T* get() const { return obj_; } |
+ |
+ T* release() { |
+ T* result = obj_; |
+ obj_ = NULL; |
+ return result; |
+ } |
+ |
+ private: |
+ T* obj_; |
+ |
+ DISALLOW_ALLOCATION(); |
+ DISALLOW_COPY_AND_ASSIGN(ScopedSSLType); |
+}; |
+ |
+template<typename T, typename E, void (*func)(E*)> |
+class ScopedSSLStackType { |
+ public: |
+ explicit ScopedSSLStackType(T* obj) : obj_(obj) {} |
+ |
+ ~ScopedSSLStackType() { |
+ if (obj_ != NULL) { |
+ sk_pop_free(reinterpret_cast<_STACK*>(obj_), |
+ reinterpret_cast<void (*)(void *)>(func)); |
+ } |
+ } |
+ |
+ T* get() { return obj_; } |
+ const T* get() const { return obj_; } |
+ |
+ T* release() { |
+ T* result = obj_; |
+ obj_ = NULL; |
+ return result; |
+ } |
+ |
+ private: |
+ T* obj_; |
+ |
+ DISALLOW_ALLOCATION(); |
+ DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType); |
+}; |
+ |
+typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12; |
+typedef ScopedSSLType<X509, X509_free> ScopedX509; |
+ |
+typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack; |
+typedef ScopedSSLStackType<STACK_OF(X509_NAME), X509_NAME, X509_NAME_free> |
+ ScopedX509NAMEStack; |
+ |
+static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) { |
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
+ if (p12.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ EVP_PKEY* key = NULL; |
+ X509 *cert = NULL; |
+ STACK_OF(X509) *ca_certs = NULL; |
+ int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); |
+ if (status == 0) { |
+ return NULL; |
+ } |
+ |
+ // We only care about the private key. |
+ ScopedX509 delete_cert(cert); |
+ ScopedX509Stack delete_ca_certs(ca_certs); |
+ return key; |
+} |
+ |
+ |
+static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) { |
+ EVP_PKEY *key = PEM_read_bio_PrivateKey( |
+ bio, NULL, PasswordCallback, const_cast<char*>(password)); |
+ |
+ // If the data doesn't contain the PEM start line, try reading as PKCS12. |
+ uint32_t err = ERR_peek_last_error(); |
+ if ((key == NULL) && |
+ (ERR_GET_LIB(err) == ERR_LIB_PEM) && |
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
+ // Reset the bio, and clear the error from trying to read as PEM. |
+ ERR_clear_error(); |
+ BIO_reset(bio); |
+ |
+ // Try to decode as PKCS12 |
+ key = GetPrivateKeyPKCS12(bio, password); |
+ } |
+ return key; |
+} |
+ |
+ |
void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)( |
Dart_NativeArguments args) { |
SSL_CTX* context = GetSecurityContext(args); |
@@ -467,9 +571,8 @@ void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)( |
int status; |
{ |
- MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
- EVP_PKEY *key = PEM_read_bio_PrivateKey( |
- bio.bio(), NULL, PasswordCallback, const_cast<char*>(password)); |
+ ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
+ EVP_PKEY *key = GetPrivateKey(bio.bio(), password); |
status = SSL_CTX_use_PrivateKey(context, key); |
} |
@@ -480,7 +583,52 @@ void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)( |
} |
-static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) { |
+static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, BIO* bio) { |
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
+ if (p12.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ EVP_PKEY* key = NULL; |
+ X509 *cert = NULL; |
+ STACK_OF(X509) *ca_certs = NULL; |
+ // There should be no private keys in this file, so we hardcode the password |
+ // to "". |
+ // TODO(zra): Allow passing a password anyway. |
+ int status = PKCS12_parse(p12.get(), "", &key, &cert, &ca_certs); |
+ if (status == 0) { |
+ return status; |
+ } |
+ |
+ ScopedX509Stack cert_stack(ca_certs); |
+ |
+ // There should be no private key. |
+ if (key != NULL) { |
+ X509_free(cert); |
+ return 0; |
+ } |
+ |
+ X509_STORE* store = SSL_CTX_get_cert_store(context); |
+ status = X509_STORE_add_cert(store, cert); |
+ if (status == 0) { |
+ X509_free(cert); |
+ return status; |
+ } |
+ |
+ X509* ca; |
+ while ((ca = sk_X509_shift(cert_stack.get())) != NULL) { |
+ status = X509_STORE_add_cert(store, ca); |
+ if (status == 0) { |
+ X509_free(ca); |
+ return status; |
+ } |
+ } |
+ |
+ return status; |
+} |
+ |
+ |
+static int SetTrustedCertificatesBytesPEM(SSL_CTX* context, BIO* bio) { |
X509_STORE* store = SSL_CTX_get_cert_store(context); |
int status = 0; |
@@ -494,11 +642,8 @@ static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) { |
} |
uint32_t err = ERR_peek_last_error(); |
- if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && |
- (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
- // Reached the end of the buffer. |
- ERR_clear_error(); |
- } else { |
+ if ((ERR_GET_LIB(err) != ERR_LIB_PEM) || |
+ (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)) { |
// Some real error happened. |
status = 0; |
} |
@@ -507,12 +652,29 @@ static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) { |
} |
+static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) { |
+ int status = SetTrustedCertificatesBytesPEM(context, bio); |
+ uint32_t err = ERR_peek_last_error(); |
+ if ((status == 0) && |
+ (ERR_GET_LIB(err) == ERR_LIB_PEM) && |
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
+ ERR_clear_error(); |
+ BIO_reset(bio); |
+ status = SetTrustedCertificatesBytesPKCS12(context, bio); |
+ } else if (status != 0) { |
+ // The PEM file was successfully parsed. |
+ ERR_clear_error(); |
+ } |
+ return status; |
+} |
+ |
+ |
void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( |
Dart_NativeArguments args) { |
SSL_CTX* context = GetSecurityContext(args); |
int status; |
{ |
- MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
+ ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
status = SetTrustedCertificatesBytes(context, bio.bio()); |
} |
CheckStatus(status, |
@@ -539,34 +701,78 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)( |
} |
-static int UseChainBytes(SSL_CTX* context, BIO* bio) { |
- int status = 0; |
- X509* x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); |
- if (x509 == NULL) { |
+static int UseChainBytesPKCS12(SSL_CTX* context, BIO* bio) { |
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
+ if (p12.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ EVP_PKEY* key = NULL; |
+ X509 *cert = NULL; |
+ STACK_OF(X509) *ca_certs = NULL; |
+ // There should be no private keys in this file, so we hardcode the password |
+ // to "". |
+ // TODO(zra): Allow passing a password anyway. |
+ int status = PKCS12_parse(p12.get(), "", &key, &cert, &ca_certs); |
+ if (status == 0) { |
+ return status; |
+ } |
+ |
+ ScopedX509 x509(cert); |
+ ScopedX509Stack certs(ca_certs); |
+ |
+ // There should be no private key. |
+ if (key != NULL) { |
return 0; |
} |
- status = SSL_CTX_use_certificate(context, x509); |
+ status = SSL_CTX_use_certificate(context, x509.get()); |
if (ERR_peek_error() != 0) { |
// Key/certificate mismatch doesn't imply status is 0. |
status = 0; |
} |
if (status == 0) { |
- X509_free(x509); |
return status; |
} |
SSL_CTX_clear_chain_certs(context); |
- while (true) { |
- X509* ca = PEM_read_bio_X509(bio, NULL, NULL, NULL); |
- if (ca == NULL) { |
- break; |
+ X509* ca; |
+ while ((ca = sk_X509_shift(certs.get())) != NULL) { |
+ status = SSL_CTX_add0_chain_cert(context, ca); |
+ if (status == 0) { |
+ X509_free(ca); |
+ return status; |
} |
+ } |
+ |
+ return status; |
+} |
+ |
+ |
+static int UseChainBytesPEM(SSL_CTX* context, BIO* bio) { |
+ int status = 0; |
+ ScopedX509 x509(PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL)); |
+ if (x509.get() == NULL) { |
+ return 0; |
+ } |
+ |
+ status = SSL_CTX_use_certificate(context, x509.get()); |
+ if (ERR_peek_error() != 0) { |
+ // Key/certificate mismatch doesn't imply status is 0. |
+ status = 0; |
+ } |
+ if (status == 0) { |
+ return status; |
+ } |
+ |
+ SSL_CTX_clear_chain_certs(context); |
+ |
+ X509* ca; |
+ while ((ca = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
status = SSL_CTX_add0_chain_cert(context, ca); |
if (status == 0) { |
X509_free(ca); |
- X509_free(x509); |
return status; |
} |
// Note that we must not free `ca` if it was successfully added to the |
@@ -575,16 +781,29 @@ static int UseChainBytes(SSL_CTX* context, BIO* bio) { |
} |
uint32_t err = ERR_peek_last_error(); |
- if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && |
- (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
- // Reached the end of the buffer. |
- ERR_clear_error(); |
- } else { |
+ if ((ERR_GET_LIB(err) != ERR_LIB_PEM) || |
+ (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)) { |
// Some real error happened. |
status = 0; |
} |
- X509_free(x509); |
+ return status; |
+} |
+ |
+ |
+static int UseChainBytes(SSL_CTX* context, BIO* bio) { |
+ int status = UseChainBytesPEM(context, bio); |
+ uint32_t err = ERR_peek_last_error(); |
+ if ((status == 0) && |
+ (ERR_GET_LIB(err) == ERR_LIB_PEM) && |
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
+ ERR_clear_error(); |
+ BIO_reset(bio); |
+ status = UseChainBytesPKCS12(context, bio); |
+ } else if (status != 0) { |
+ // The PEM file was successfully parsed. |
+ ERR_clear_error(); |
+ } |
return status; |
} |
@@ -594,7 +813,7 @@ void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( |
SSL_CTX* context = GetSecurityContext(args); |
int status; |
{ |
- MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
+ ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
status = UseChainBytes(context, bio.bio()); |
} |
CheckStatus(status, |
@@ -603,36 +822,120 @@ void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( |
} |
-static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio) { |
- STACK_OF(X509_NAME)* result = sk_X509_NAME_new_null(); |
- if (result == NULL) { |
+static STACK_OF(X509_NAME)* GetCertificateNamesPKCS12(BIO* bio) { |
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
+ if (p12.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ ScopedX509NAMEStack result(sk_X509_NAME_new_null()); |
+ if (result.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ EVP_PKEY* key = NULL; |
+ X509 *cert = NULL; |
+ STACK_OF(X509) *ca_certs = NULL; |
+ // There should be no private keys in this file, so we hardcode the password |
+ // to "". |
+ // TODO(zra): Allow passing a password anyway. |
+ int status = PKCS12_parse(p12.get(), "", &key, &cert, &ca_certs); |
+ if (status == 0) { |
+ return NULL; |
+ } |
+ |
+ ScopedX509 x509(cert); |
+ ScopedX509Stack certs(ca_certs); |
+ |
+ // There should be no private key. |
+ if (key != NULL) { |
+ return NULL; |
+ } |
+ |
+ X509_NAME* x509_name = X509_get_subject_name(x509.get()); |
+ if (x509_name == NULL) { |
+ return NULL; |
+ } |
+ |
+ x509_name = X509_NAME_dup(x509_name); |
+ if (x509_name == NULL) { |
+ return NULL; |
+ } |
+ |
+ sk_X509_NAME_push(result.get(), x509_name); |
+ |
+ while (true) { |
+ ScopedX509 ca(sk_X509_shift(certs.get())); |
+ if (ca.get() == NULL) { |
+ break; |
+ } |
+ |
+ X509_NAME* x509_name = X509_get_subject_name(ca.get()); |
+ if (x509_name == NULL) { |
+ return NULL; |
+ } |
+ |
+ x509_name = X509_NAME_dup(x509_name); |
+ if (x509_name == NULL) { |
+ return NULL; |
+ } |
+ |
+ sk_X509_NAME_push(result.get(), x509_name); |
+ } |
+ |
+ return result.release(); |
+} |
+ |
+ |
+static STACK_OF(X509_NAME)* GetCertificateNamesPEM(BIO* bio) { |
+ ScopedX509NAMEStack result(sk_X509_NAME_new_null()); |
+ if (result.get() == NULL) { |
return NULL; |
} |
while (true) { |
- X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); |
- if (x509 == NULL) { |
+ ScopedX509 x509(PEM_read_bio_X509(bio, NULL, NULL, NULL)); |
+ if (x509.get() == NULL) { |
break; |
} |
- X509_NAME* x509_name = X509_get_subject_name(x509); |
+ X509_NAME* x509_name = X509_get_subject_name(x509.get()); |
if (x509_name == NULL) { |
- sk_X509_NAME_pop_free(result, X509_NAME_free); |
- X509_free(x509); |
return NULL; |
} |
// Duplicate the name to put it on the stack. |
x509_name = X509_NAME_dup(x509_name); |
if (x509_name == NULL) { |
- sk_X509_NAME_pop_free(result, X509_NAME_free); |
- X509_free(x509); |
return NULL; |
} |
- sk_X509_NAME_push(result, x509_name); |
- X509_free(x509); |
+ sk_X509_NAME_push(result.get(), x509_name); |
+ } |
+ |
+ uint32_t err = ERR_peek_last_error(); |
+ if ((ERR_GET_LIB(err) != ERR_LIB_PEM) || |
+ (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)) { |
+ // Some real error happened. |
+ return NULL; |
} |
+ return result.release(); |
+} |
+ |
+ |
+static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio) { |
+ STACK_OF(X509_NAME)* result = GetCertificateNamesPEM(bio); |
+ uint32_t err = ERR_peek_last_error(); |
+ if ((result == NULL) && |
+ (ERR_GET_LIB(err) == ERR_LIB_PEM) && |
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
+ ERR_clear_error(); |
+ BIO_reset(bio); |
+ result = GetCertificateNamesPKCS12(bio); |
+ } else if (result != NULL) { |
+ // The PEM file was successfully parsed. |
+ ERR_clear_error(); |
+ } |
return result; |
} |
@@ -643,7 +946,7 @@ void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( |
STACK_OF(X509_NAME)* certificate_names; |
{ |
- MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
+ ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
certificate_names = GetCertificateNames(bio.bio()); |
} |