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.

Signed-off-by: Rubin Xu <rubinxu@google.com>
This commit is contained in:
Rubin Xu 2015-11-10 17:14:51 +00:00 committed by Jouni Malinen
parent 561536205a
commit a8ef133f1d

View file

@ -105,6 +105,66 @@ static BIO * BIO_from_keystore(const char *key)
free(value); free(value);
return bio; 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);
unsigned char *decoded_alias;
if (len & 1) {
wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
encoded_key_alias);
return rc;
}
decoded_alias = os_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, (const char *) decoded_alias);
}
os_free(decoded_alias);
}
return rc;
}
#endif /* ANDROID */ #endif /* ANDROID */
static int tls_openssl_ref_count = 0; static int tls_openssl_ref_count = 0;
@ -1989,30 +2049,40 @@ static int tls_connection_ca_cert(struct tls_data *data,
} }
#ifdef ANDROID #ifdef ANDROID
/* Single alias */
if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
BIO *bio = BIO_from_keystore(&ca_cert[11]); if (tls_add_ca_from_keystore(ssl_ctx->cert_store,
STACK_OF(X509_INFO) *stack = NULL; &ca_cert[11]) < 0)
stack_index_t i;
if (bio) {
stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
BIO_free(bio);
}
if (!stack)
return -1; return -1;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0;
}
for (i = 0; i < sk_X509_INFO_num(stack); ++i) { /* Multiple aliases separated by space */
X509_INFO *info = sk_X509_INFO_value(stack, i); if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
if (info->x509) { char *aliases = os_strdup(&ca_cert[12]);
X509_STORE_add_cert(ssl_ctx->cert_store, const char *delim = " ";
info->x509); int rc = 0;
} char *savedptr;
if (info->crl) { char *alias;
X509_STORE_add_crl(ssl_ctx->cert_store,
info->crl); if (!aliases)
return -1;
alias = strtok_r(aliases, delim, &savedptr);
for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
if (tls_add_ca_from_keystore_encoded(
ssl_ctx->cert_store, alias)) {
wpa_printf(MSG_WARNING,
"OpenSSL: %s - Failed to add ca_cert %s from keystore",
__func__, alias);
rc = -1;
break;
} }
} }
sk_X509_INFO_pop_free(stack, X509_INFO_free); os_free(aliases);
if (rc)
return rc;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0; return 0;
} }