[PATCH 6/6] [RFC] library: Add openconnect_get_peer_cert_chain()
Kevin Cernekee
cernekee at gmail.com
Thu Apr 14 23:06:05 PDT 2016
Allow external validation of the entire certificate chain, not just the
peer_cert. Tested using a letsencrypt cert on Chrome OS.
Signed-off-by: Kevin Cernekee <cernekee at gmail.com>
---
libopenconnect.map.in | 2 ++
openconnect-internal.h | 1 +
openconnect.h | 16 +++++++++++++++
openssl.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/libopenconnect.map.in b/libopenconnect.map.in
index 1e33389..8c48283 100644
--- a/libopenconnect.map.in
+++ b/libopenconnect.map.in
@@ -3,6 +3,7 @@ OPENCONNECT_5.0 {
openconnect_check_peer_cert_hash;
openconnect_clear_cookie;
openconnect_free_cert_info;
+ openconnect_free_peer_cert_chain;
openconnect_get_cookie;
openconnect_get_cstp_cipher;
openconnect_get_dnsname;
@@ -11,6 +12,7 @@ OPENCONNECT_5.0 {
openconnect_get_ifname;
openconnect_get_ip_info;
openconnect_get_peer_cert_DER;
+ openconnect_get_peer_cert_chain;
openconnect_get_peer_cert_details;
openconnect_get_peer_cert_hash;
openconnect_get_port;
diff --git a/openconnect-internal.h b/openconnect-internal.h
index b339ef6..27f9df8 100644
--- a/openconnect-internal.h
+++ b/openconnect-internal.h
@@ -443,6 +443,7 @@ struct openconnect_info {
void *peer_cert;
char *peer_cert_hash;
+ void *cert_chain_handle;
char *cookie; /* Pointer to within cookies list */
struct oc_vpn_option *cookies;
diff --git a/openconnect.h b/openconnect.h
index f7d4fe2..bcbcb74 100644
--- a/openconnect.h
+++ b/openconnect.h
@@ -273,6 +273,12 @@ struct oc_stats {
uint64_t rx_bytes;
};
+struct oc_cert {
+ int der_len;
+ unsigned char *der_data;
+ void *reserved;
+};
+
/****************************************************************************/
#define PRG_ERR 0
@@ -367,6 +373,16 @@ int openconnect_get_peer_cert_DER(struct openconnect_info *vpninfo,
unsigned char **buf);
void openconnect_free_cert_info(struct openconnect_info *vpninfo,
void *buf);
+
+/* Creates a linked list of all certs in the peer's chain, returning the
+ number of certs in the chain (or <0 on error). Only valid inside the
+ validate_peer_cert callback. */
+int openconnect_get_peer_cert_chain(struct openconnect_info *vpninfo,
+ struct oc_cert **chain);
+void openconnect_free_peer_cert_chain(struct openconnect_info *vpninfo,
+ int ncerts,
+ struct oc_cert *chain);
+
/* Contains a comma-separated list of authentication methods to enabled.
Currently supported: Negotiate,NTLM,Digest,Basic */
int openconnect_set_http_auth(struct openconnect_info *vpninfo,
diff --git a/openssl.c b/openssl.c
index 007809b..05cc973 100644
--- a/openssl.c
+++ b/openssl.c
@@ -1325,6 +1325,49 @@ static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
X509_STORE_CTX_cleanup(&ctx);
}
+int openconnect_get_peer_cert_chain(struct openconnect_info *vpninfo,
+ struct oc_cert **chainp)
+{
+ struct oc_cert *chain, *p;
+ X509_STORE_CTX *ctx = vpninfo->cert_chain_handle;
+ int i, ncerts;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ncerts = sk_X509_num(ctx->untrusted);
+ if (!ncerts)
+ return -EIO;
+
+ p = chain = calloc(ncerts, sizeof(struct oc_cert));
+ if (!chain)
+ return -ENOMEM;
+
+ for (i = 0; i < ncerts; i++, p++) {
+ X509 *cert = sk_X509_value(ctx->untrusted, i);
+
+ p->der_len = i2d_X509(cert, &p->der_data);
+ if (p->der_len < 0) {
+ openconnect_free_peer_cert_chain(vpninfo, ncerts, chain);
+ return -ENOMEM;
+ }
+ }
+
+ *chainp = chain;
+ return ncerts;
+}
+
+void openconnect_free_peer_cert_chain(struct openconnect_info *vpninfo,
+ int ncerts,
+ struct oc_cert *chain)
+{
+ int i;
+
+ for (i = 0; i < ncerts; i++)
+ OPENSSL_free(chain[i].der_data);
+ free(chain);
+}
+
static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
{
struct openconnect_info *vpninfo = arg;
@@ -1375,9 +1418,16 @@ static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
_("Server certificate verify failed: %s\n"),
err_string);
- if (vpninfo->validate_peer_cert &&
- !vpninfo->validate_peer_cert(vpninfo->cbdata, err_string))
- return 1;
+ if (vpninfo->validate_peer_cert) {
+ int ret;
+
+ vpninfo->cert_chain_handle = ctx;
+ ret = vpninfo->validate_peer_cert(vpninfo->cbdata, err_string);
+ vpninfo->cert_chain_handle = NULL;
+
+ if (!ret)
+ return 1;
+ }
return 0;
}
--
1.9.1
More information about the openconnect-devel
mailing list