From 5b2001ab6f7a239f7d7e828ca5b7a7e5f842ca5e Mon Sep 17 00:00:00 2001 From: Vilmos Nebehaj Date: Wed, 28 Dec 2011 19:59:29 +0000 Subject: [PATCH] Add Android keystore support. BIO_from_keystore() can be used to retrieve certificates/private keys from the android keystore. Introduce new certificate type 'CERT_TYPE_KEYSTORE', that refers to keys and certificates retrived from the android keystore service. They can be acquired once the system keystore has been unlocked. Certificates can be added through the android system settings application. In ssl.c, make the functions doing the actual retrieval general by only requiring a vpninfo struct and a BIO. This minimizes android-specific code. Signed-off-by: Vilmos Nebehaj --- main.c | 4 +- openconnect-internal.h | 1 + ssl.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index a18e34c..1acc15e 100644 --- a/main.c +++ b/main.c @@ -164,7 +164,7 @@ static void usage(void) printf(" -c, --certificate=CERT %s\n", _("Use SSL client certificate CERT")); printf(" -e, --cert-expire-warning=DAYS %s\n", _("Warn when certificate lifetime < DAYS")); printf(" -k, --sslkey=KEY %s\n", _("Use SSL private key file KEY")); - printf(" -K, --key-type=TYPE %s\n", _("Private key type (PKCS#12 / TPM / PEM)")); + printf(" -K, --key-type=TYPE %s\n", _("Private key type (PKCS#12 / TPM / PEM / KEYSTORE)")); printf(" -C, --cookie=COOKIE %s\n", _("Use WebVPN cookie COOKIE")); printf(" --cookie-on-stdin %s\n", _("Read cookie from standard input")); printf(" -d, --deflate %s\n", _("Enable compression (default)")); @@ -368,6 +368,8 @@ int main(int argc, char **argv) vpninfo->cert_type = CERT_TYPE_TPM; } else if (!strcasecmp(optarg, "PEM")) { vpninfo->cert_type = CERT_TYPE_PEM; + } else if (!strcasecmp(optarg, "KEYSTORE")) { + vpninfo->cert_type = CERT_TYPE_KEYSTORE; } else { fprintf(stderr, _("Unknown certificate type '%s'\n"), optarg); diff --git a/openconnect-internal.h b/openconnect-internal.h index 05dea2a..61c8087 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -91,6 +91,7 @@ struct split_include { #define CERT_TYPE_PEM 1 #define CERT_TYPE_PKCS12 2 #define CERT_TYPE_TPM 3 +#define CERT_TYPE_KEYSTORE 4 struct openconnect_info { char *redirect_url; diff --git a/ssl.c b/ssl.c index a305ace..259511e 100644 --- a/ssl.c +++ b/ssl.c @@ -50,6 +50,10 @@ #include #include +#ifdef ANDROID +#include "keystore_get.h" +#endif + #include "openconnect-internal.h" /* OSX < 1.6 doesn't have AI_NUMERICSERV */ @@ -300,11 +304,112 @@ static int reload_pem_cert(struct openconnect_info *vpninfo) return 0; } +#ifdef ANDROID +static BIO *BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + char value[KEYSTORE_MESSAGE_SIZE]; + int length = keystore_get(key, strlen(key), value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) { + BIO_write(bio, value, length); + } + return bio; +} + +static int load_bio_certificate(struct openconnect_info *vpninfo, + BIO *cert_bio, BIO *key_bio) +{ + EVP_PKEY *evp = PEM_read_bio_PrivateKey(key_bio, NULL, NULL, NULL); + BIO_free(key_bio); + + vpninfo->cert_x509 = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL); + BIO_free(cert_bio); + + if (!vpninfo->cert_x509 || + !SSL_CTX_use_certificate(vpninfo->https_ctx, vpninfo->cert_x509)) { + report_ssl_errors(vpninfo); + vpn_progress(vpninfo, PRG_ERR, + _("Loading certificate failed\n")); + if (vpninfo->cert_x509) { + X509_free(vpninfo->cert_x509); + vpninfo->cert_x509 = NULL; + } + BIO_free(key_bio); + return -ENOENT; + } + + if (!evp || !SSL_CTX_use_PrivateKey(vpninfo->https_ctx, evp)) { + report_ssl_errors(vpninfo); + vpn_progress(vpninfo, PRG_ERR, + _("Loading private key failed\n")); + if (evp) + EVP_PKEY_free(evp); + X509_free(vpninfo->cert_x509); + vpninfo->cert_x509 = NULL; + return -ENOENT; + } + + EVP_PKEY_free(evp); + + return 0; +} + +static int load_bio_cacert(struct openconnect_info *vpninfo, BIO *bio) +{ + int i; + X509_STORE *cert_ctx; + STACK_OF(X509_INFO) *stack; + + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + + if (!stack) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to open CA file '%s'\n"), + vpninfo->cafile); + report_ssl_errors(vpninfo); + return -ENOENT; + } + + cert_ctx = X509_STORE_new(); + if (cert_ctx == NULL) { + vpn_progress(vpninfo, PRG_ERR, + _("X509_STORE_new failed\n")); + report_ssl_errors(vpninfo); + return -ENOMEM; + } + + 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(cert_ctx, info->x509); + } + if (info->crl) { + X509_STORE_add_crl(cert_ctx, info->crl); + } + } + sk_X509_INFO_pop_free(stack, X509_INFO_free); + + SSL_CTX_set_cert_store(vpninfo->https_ctx, cert_ctx); + return 0; +} +#endif + static int load_certificate(struct openconnect_info *vpninfo) { vpn_progress(vpninfo, PRG_TRACE, _("Using certificate file %s\n"), vpninfo->cert); +#ifdef ANDROID + if (vpninfo->cert_type == CERT_TYPE_KEYSTORE || + vpninfo->cert_type == CERT_TYPE_UNKNOWN) { + BIO *b = BIO_from_keystore(vpninfo->cert); + if (b) + return load_bio_certificate(vpninfo, b, + BIO_from_keystore(vpninfo->sslkey)); + } +#endif + if (vpninfo->cert_type == CERT_TYPE_PKCS12 || vpninfo->cert_type == CERT_TYPE_UNKNOWN) { FILE *f; @@ -1070,7 +1175,23 @@ int openconnect_open_https(struct openconnect_info *vpninfo) SSL_CTX_set_default_verify_paths(vpninfo->https_ctx); if (vpninfo->cafile) { - if (!SSL_CTX_load_verify_locations(vpninfo->https_ctx, vpninfo->cafile, NULL)) { + err = 1; +#ifdef ANDROID + /* + * Try to load CA certificate from keystore, but just fall + * through if this fails. + */ + BIO *bio = BIO_from_keystore(vpninfo->cafile); + if (bio) { + err = load_bio_cacert(vpninfo, bio); + if (err) { + close(ssl_sock); + return err; + } + } +#endif + if (err && + !SSL_CTX_load_verify_locations(vpninfo->https_ctx, vpninfo->cafile, NULL)) { vpn_progress(vpninfo, PRG_ERR, _("Failed to open CA file '%s'\n"), vpninfo->cafile); @@ -1079,7 +1200,6 @@ int openconnect_open_https(struct openconnect_info *vpninfo) return -EINVAL; } } - } https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); -- 1.7.5.4