Skip to content

Commit

Permalink
Fix lifetime of cstp_cipher and peer_cert_hash strings
Browse files Browse the repository at this point in the history
Just calculate these during the handshake and keep them around until
the next connection is opened. We don't want to kill them in
openconnect_close_https() because we might want to know what they were
even after a HTTP/1.0 fetch or Connection: Close.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Nov 4, 2014
1 parent 5b9c521 commit b91b4a9
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 98 deletions.
103 changes: 48 additions & 55 deletions gnutls.c
Expand Up @@ -1653,49 +1653,48 @@ int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_MD5, buf);
}

const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo)
int set_peer_cert_hash(struct openconnect_info *vpninfo)
{
if (!vpninfo->peer_cert)
return NULL;

if (!vpninfo->peer_cert_hash) {
unsigned char sha1[SHA1_SIZE];
size_t shalen = SHA1_SIZE;
gnutls_pubkey_t pkey;
gnutls_datum_t d;
int i;

if (gnutls_pubkey_init(&pkey))
return NULL;
unsigned char sha1[SHA1_SIZE];
size_t shalen = SHA1_SIZE;
gnutls_pubkey_t pkey;
gnutls_datum_t d;
int i, err;

if (gnutls_pubkey_import_x509(pkey, vpninfo->peer_cert, 0)) {
gnutls_pubkey_deinit(pkey);
return NULL;
}
err = gnutls_pubkey_init(&pkey);
if (err)
return err;

if (gnutls_pubkey_export2(pkey, GNUTLS_X509_FMT_DER, &d)) {
gnutls_pubkey_deinit(pkey);
return NULL;
}
err = gnutls_pubkey_import_x509(pkey, vpninfo->peer_cert, 0);
if (err) {
gnutls_pubkey_deinit(pkey);
return err;
}

err = gnutls_pubkey_export2(pkey, GNUTLS_X509_FMT_DER, &d);
if (err) {
gnutls_pubkey_deinit(pkey);
return err;
}

if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, sha1, &shalen)) {
gnutls_free(d.data);
return NULL;
}
gnutls_pubkey_deinit(pkey);

err = gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, sha1, &shalen);
if (err) {
gnutls_free(d.data);
return err;
}

vpninfo->peer_cert_hash = malloc(SHA1_SIZE * 2 + 6);
if (vpninfo->peer_cert_hash) {
snprintf(vpninfo->peer_cert_hash, 6, "sha1:");
for (i = 0; i < shalen; i++)
sprintf(&vpninfo->peer_cert_hash[i*2 + 5], "%02x", sha1[i]);
}
gnutls_free(d.data);

vpninfo->peer_cert_hash = malloc(SHA1_SIZE * 2 + 6);
if (vpninfo->peer_cert_hash) {
snprintf(vpninfo->peer_cert_hash, 6, "sha1:");
for (i = 0; i < shalen; i++)
sprintf(&vpninfo->peer_cert_hash[i*2 + 5], "%02x", sha1[i]);
}

return vpninfo->peer_cert_hash;
return 0;
}

char *openconnect_get_peer_cert_details(struct openconnect_info *vpninfo)
Expand Down Expand Up @@ -1746,11 +1745,6 @@ static int verify_peer(gnutls_session_t session)
const char *reason = NULL;
int err = 0;

if (vpninfo->peer_cert) {
gnutls_x509_crt_deinit(vpninfo->peer_cert);
vpninfo->peer_cert = NULL;
}

cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
if (!cert_list) {
vpn_progress(vpninfo, PRG_ERR, _("Server presented no certificate\n"));
Expand All @@ -1771,8 +1765,11 @@ static int verify_peer(gnutls_session_t session)
}

vpninfo->peer_cert = cert;
free(vpninfo->peer_cert_hash);
vpninfo->peer_cert_hash = 0;
err = set_peer_cert_hash(vpninfo);
if (err < 0) {
vpn_progress(vpninfo, PRG_ERR,
_("Could not calculate hash of server's certificate\n"));
}

if (vpninfo->servercert) {
err = openconnect_check_peer_cert_hash(vpninfo, vpninfo->servercert);
Expand Down Expand Up @@ -1903,6 +1900,15 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
if (vpninfo->https_sess)
return 0;

if (vpninfo->peer_cert) {
gnutls_x509_crt_deinit(vpninfo->peer_cert);
vpninfo->peer_cert = NULL;
}
free(vpninfo->peer_cert_hash);
vpninfo->peer_cert_hash = 0;
gnutls_free(vpninfo->cstp_cipher);
vpninfo->cstp_cipher = NULL;

ssl_sock = connect_https_socket(vpninfo);
if (ssl_sock < 0)
return ssl_sock;
Expand Down Expand Up @@ -2048,6 +2054,9 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
if (err)
return err;

gnutls_free(vpninfo->cstp_cipher);
vpninfo->cstp_cipher = get_gnutls_cipher(vpninfo->https_sess);

vpninfo->ssl_fd = ssl_sock;

vpninfo->ssl_read = openconnect_gnutls_read;
Expand Down Expand Up @@ -2114,13 +2123,6 @@ int cstp_handshake(struct openconnect_info *vpninfo, unsigned init)

void openconnect_close_https(struct openconnect_info *vpninfo, int final)
{
if (vpninfo->peer_cert) {
gnutls_x509_crt_deinit(vpninfo->peer_cert);
vpninfo->peer_cert = NULL;
}
free(vpninfo->peer_cert_hash);
vpninfo->peer_cert_hash = NULL;

if (vpninfo->https_sess) {
gnutls_deinit(vpninfo->https_sess);
vpninfo->https_sess = NULL;
Expand Down Expand Up @@ -2222,15 +2224,6 @@ char *get_gnutls_cipher(gnutls_session_t session)
return str;
}

const char *openconnect_get_cstp_cipher(struct openconnect_info *vpninfo)
{
if (vpninfo->gnutls_cstp_cipher)
gnutls_free(vpninfo->gnutls_cstp_cipher);

vpninfo->gnutls_cstp_cipher = get_gnutls_cipher(vpninfo->https_sess);
return vpninfo->gnutls_cstp_cipher;
}

int openconnect_sha1(unsigned char *result, void *data, int datalen)
{
gnutls_datum_t d;
Expand Down
12 changes: 11 additions & 1 deletion library.c
Expand Up @@ -198,7 +198,7 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
free(vpninfo->ifname);
free(vpninfo->dtls_cipher);
#if defined(OPENCONNECT_GNUTLS)
gnutls_free(vpninfo->gnutls_cstp_cipher);
gnutls_free(vpninfo->cstp_cipher); /* In OpenSSL this is const */
gnutls_free(vpninfo->gnutls_dtls_cipher);
#endif
free(vpninfo->dtls_addr);
Expand Down Expand Up @@ -757,3 +757,13 @@ int openconnect_check_peer_cert_hash(struct openconnect_info *vpninfo,

return 0;
}

const char *openconnect_get_cstp_cipher(struct openconnect_info *vpninfo)
{
return vpninfo->cstp_cipher;
}

const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo)
{
return vpninfo->peer_cert_hash;
}
3 changes: 2 additions & 1 deletion openconnect-internal.h
Expand Up @@ -348,8 +348,9 @@ struct openconnect_info {
have fewer ifdefs and accessor macros for it. */
gnutls_session_t dtls_ssl;
char *gnutls_dtls_cipher; /* cached for openconnect_get_dtls_cipher() */
char *gnutls_cstp_cipher;
#endif
char *cstp_cipher;

int dtls_state;
struct keepalive_info dtls_times;
unsigned char dtls_session_id[32];
Expand Down
70 changes: 29 additions & 41 deletions openssl.c
Expand Up @@ -891,44 +891,40 @@ int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
return get_cert_fingerprint(vpninfo, cert, EVP_md5(), buf);
}

const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo)
static int set_peer_cert_hash(struct openconnect_info *vpninfo)
{
if (!vpninfo->peer_cert)
return NULL;

if (!vpninfo->peer_cert_hash) {
unsigned char sha1[SHA1_SIZE];
EVP_PKEY *pkey;
BIO *bp = BIO_new(BIO_s_mem());
BUF_MEM *keyinfo;
int i;
unsigned char sha1[SHA1_SIZE];
EVP_PKEY *pkey;
BIO *bp = BIO_new(BIO_s_mem());
BUF_MEM *keyinfo;
int i;

/* We can't use X509_pubkey_digest() because it only hashes the
subjectPublicKey BIT STRING, and not the whole of the
SubjectPublicKeyInfo SEQUENCE. */
pkey = X509_get_pubkey(vpninfo->peer_cert);
/* We can't use X509_pubkey_digest() because it only hashes the
subjectPublicKey BIT STRING, and not the whole of the
SubjectPublicKeyInfo SEQUENCE. */
pkey = X509_get_pubkey(vpninfo->peer_cert);

if (!i2d_PUBKEY_bio(bp, pkey)) {
EVP_PKEY_free(pkey);
BIO_free(bp);
return NULL;
}
if (!i2d_PUBKEY_bio(bp, pkey)) {
EVP_PKEY_free(pkey);
BIO_free(bp);
return -ENOMEM;
}
EVP_PKEY_free(pkey);

BIO_get_mem_ptr(bp, &keyinfo);
BIO_get_mem_ptr(bp, &keyinfo);

openconnect_sha1(sha1, keyinfo->data, keyinfo->length);
openconnect_sha1(sha1, keyinfo->data, keyinfo->length);

BIO_free(bp);
BIO_free(bp);

vpninfo->peer_cert_hash = malloc(SHA1_SIZE * 2 + 6);
if (vpninfo->peer_cert_hash) {
snprintf(vpninfo->peer_cert_hash, 6, "sha1:");
for (i = 0; i < sizeof(sha1); i++)
sprintf(&vpninfo->peer_cert_hash[i*2 + 5], "%02x", sha1[i]);
}
vpninfo->peer_cert_hash = malloc(SHA1_SIZE * 2 + 6);
if (vpninfo->peer_cert_hash) {
snprintf(vpninfo->peer_cert_hash, 6, "sha1:");
for (i = 0; i < sizeof(sha1); i++)
sprintf(&vpninfo->peer_cert_hash[i*2 + 5], "%02x", sha1[i]);
}
return vpninfo->peer_cert_hash;

return 0;
}

static int match_hostname_elem(const char *hostname, int helem_len,
Expand Down Expand Up @@ -1338,6 +1334,7 @@ static int check_certificate_expiry(struct openconnect_info *vpninfo)
}
return 0;
}

int openconnect_open_https(struct openconnect_info *vpninfo)
{
method_const SSL_METHOD *ssl3_method;
Expand All @@ -1355,6 +1352,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
}
free (vpninfo->peer_cert_hash);
vpninfo->peer_cert_hash = NULL;
vpninfo->cstp_cipher = NULL;

ssl_sock = connect_https_socket(vpninfo);
if (ssl_sock < 0)
Expand Down Expand Up @@ -1512,7 +1510,9 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
}
}

vpninfo->cstp_cipher = (char *)SSL_get_cipher_name(https_ssl);
vpninfo->peer_cert = SSL_get_peer_certificate(https_ssl);
set_peer_cert_hash(vpninfo);

if (verify_peer(vpninfo, https_ssl)) {
SSL_free(https_ssl);
Expand Down Expand Up @@ -1541,13 +1541,6 @@ int cstp_handshake(struct openconnect_info *vpninfo, unsigned init)

void openconnect_close_https(struct openconnect_info *vpninfo, int final)
{
if (vpninfo->peer_cert) {
X509_free(vpninfo->peer_cert);
vpninfo->peer_cert = NULL;
}
free (vpninfo->peer_cert_hash);
vpninfo->peer_cert_hash = NULL;

if (vpninfo->https_ssl) {
SSL_free(vpninfo->https_ssl);
vpninfo->https_ssl = NULL;
Expand Down Expand Up @@ -1620,8 +1613,3 @@ int openconnect_local_cert_md5(struct openconnect_info *vpninfo,

return 0;
}

const char *openconnect_get_cstp_cipher(struct openconnect_info *vpninfo)
{
return SSL_get_cipher_name(vpninfo->https_ssl);
}

0 comments on commit b91b4a9

Please sign in to comment.