>From dd90559fa3bfa3d606b51f97a66afff2ab4758d2 Mon Sep 17 00:00:00 2001 From: Rubin Xu Date: Tue, 10 Nov 2015 17:14:51 +0000 Subject: [PATCH] Android: Support multiple CA certs when connecting to EAP network In the Android-specific case, Make ca_cert directive parse a space-separated list of hex-encoded CA certificate aliases following the "keystores://" prefix. Server certificate validation should succeed as long as the chain ends with one of them. Bug: 22547958 Change-Id: I9c98f06f8ddf94ea1332f7ac291a14b7f1d96406 Signed-off-by: Rubin Xu --- src/crypto/tls_openssl.c | 98 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index b16b519..087210d 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -105,6 +105,56 @@ static BIO * BIO_from_keystore(const char *key) free(value); return bio; } + +static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias) +{ + BIO *bio = BIO_from_keystore(key_alias); + STACK_OF(X509_INFO) *stack = NULL; + stack_index_t i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (!stack) { + wpa_printf(MSG_WARNING, "TLS: failed to parse certificate: %s", key_alias); + return -1; + } + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + if (info->x509) { + X509_STORE_add_cert(ctx, + info->x509); + } + if (info->crl) { + X509_STORE_add_crl(ctx, + info->crl); + } + } + sk_X509_INFO_pop_free(stack, X509_INFO_free); + return 0; +} + +static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx, const char *encoded_key_alias) +{ + int rc = -1; + int len = os_strlen(encoded_key_alias); + if (len & 1) { + // Hex-encoded string should have an even number of characters. + wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s", encoded_key_alias); + return rc; + } + unsigned char* decoded_alias = malloc(len / 2 + 1); + if (decoded_alias) { + if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) { + decoded_alias[len / 2] = '\0'; + rc = tls_add_ca_from_keystore(ctx, (char*) decoded_alias); + } + free(decoded_alias); + } + return rc; +} #endif /* ANDROID */ static int tls_openssl_ref_count = 0; @@ -1989,32 +2039,36 @@ static int tls_connection_ca_cert(struct tls_data *data, } #ifdef ANDROID + /* Single alias */ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { - BIO *bio = BIO_from_keystore(&ca_cert[11]); - STACK_OF(X509_INFO) *stack = NULL; - stack_index_t i; - - if (bio) { - stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (!stack) + if (!tls_add_ca_from_keystore(ssl_ctx->cert_store, &ca_cert[11])) { + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } else { return -1; - - for (i = 0; i < sk_X509_INFO_num(stack); ++i) { - X509_INFO *info = sk_X509_INFO_value(stack, i); - if (info->x509) { - X509_STORE_add_cert(ssl_ctx->cert_store, - info->x509); - } - if (info->crl) { - X509_STORE_add_crl(ssl_ctx->cert_store, - info->crl); + } + /* Multiple aliases separated by space */ + } else if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) { + char *aliases = strdup(&ca_cert[12]); + const char *DELIMITER = " "; + int rc = 0; + char *savedptr; + char *alias = strtok_r(aliases, DELIMITER, &savedptr); + for(; alias; alias = strtok_r(NULL, DELIMITER, &savedptr)) { + if (tls_add_ca_from_keystore_encoded(ssl_ctx->cert_store, alias)) { + wpa_printf(MSG_WARNING, "OpenSSL: %s - fail to add ca_cert" + " %s from keystore", __func__, alias); + rc = -1; + break; } } - sk_X509_INFO_pop_free(stack, X509_INFO_free); - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); - return 0; + free(aliases); + if (!rc) { + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } else { + return -1; + } } #endif /* ANDROID */ -- 2.7.0.rc3.207.g0ac5344