Commit 4971447f authored by David Woodhouse's avatar David Woodhouse

Attempt ESP negotiation

Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 85a59615
......@@ -25,7 +25,7 @@ openconnect_LDADD = libopenconnect.la $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(INTL_LI
library_srcs = ssl.c http.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c
lib_srcs_cisco = auth.c cstp.c dtls.c
lib_srcs_juniper = oncp.c
lib_srcs_juniper = oncp.c gnutls-esp.c
lib_srcs_gnutls = gnutls.c gnutls_pkcs12.c gnutls_tpm.c
lib_srcs_openssl = openssl.c openssl-pkcs11.c
lib_srcs_win32 = tun-win32.c sspi.c
......
/*
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2008-2015 Intel Corporation.
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <config.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include "openconnect-internal.h"
void destroy_esp_ciphers(struct esp *esp)
{
if (esp->cipher) {
gnutls_cipher_deinit(esp->cipher);
esp->cipher = NULL;
}
if (esp->hmac) {
gnutls_hmac_deinit(esp->hmac, NULL);
esp->hmac = NULL;
}
}
static int init_esp_ciphers(struct openconnect_info *vpninfo, struct esp *esp,
gnutls_mac_algorithm_t macalg, gnutls_cipher_algorithm_t encalg)
{
gnutls_datum_t enc_key;
int err;
/* ∄ gnutls_cipher_get_key_size() */
if (encalg == GNUTLS_CIPHER_AES_128_CBC)
enc_key.size = 16;
else if (encalg == GNUTLS_CIPHER_AES_256_CBC)
enc_key.size = 32;
else
return -EINVAL;
enc_key.data = esp->secrets;
err = gnutls_cipher_init(&esp->cipher, encalg, &enc_key, NULL);
if (err) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to initialise ESP cipher: %s\n"),
gnutls_strerror(err));
return -EIO;
}
err = gnutls_hmac_init(&esp->hmac, macalg,
esp->secrets + enc_key.size,
gnutls_hmac_get_len(macalg));
if (err) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to initialize ESP HMAC: %s\n"),
gnutls_strerror(err));
destroy_esp_ciphers(esp);
}
return 0;
}
int setup_esp_keys(struct openconnect_info *vpninfo)
{
gnutls_mac_algorithm_t macalg;
gnutls_cipher_algorithm_t encalg;
int ret;
if (vpninfo->dtls_state == DTLS_DISABLED)
return -EOPNOTSUPP;
switch (vpninfo->esp_enc) {
case 0x02:
encalg = GNUTLS_CIPHER_AES_128_CBC;
break;
case 0x05:
encalg = GNUTLS_CIPHER_AES_256_CBC;
break;
default:
return -EINVAL;
}
switch (vpninfo->esp_hmac) {
case 0x01:
macalg = GNUTLS_MAC_MD5;
break;
case 0x02:
macalg = GNUTLS_MAC_SHA1;
break;
default:
return -EINVAL;
}
ret = gnutls_rnd(GNUTLS_RND_NONCE, &vpninfo->esp_in.secrets,
sizeof(vpninfo->esp_in.secrets));
if (ret) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to generate random keys for ESP: %s\n"),
gnutls_strerror(ret));
return -EIO;
}
ret = init_esp_ciphers(vpninfo, &vpninfo->esp_out, macalg, encalg);
if (ret)
return ret;
ret = init_esp_ciphers(vpninfo, &vpninfo->esp_in, macalg, encalg);
if (ret) {
destroy_esp_ciphers(&vpninfo->esp_out);
return ret;
}
vpninfo->dtls_state = DTLS_SECRET;
return 0;
}
......@@ -255,6 +255,8 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
#ifdef DTLS_GNUTLS
gnutls_free(vpninfo->gnutls_dtls_cipher);
#endif
destroy_esp_ciphers(&vpninfo->esp_in);
destroy_esp_ciphers(&vpninfo->esp_out);
free(vpninfo->dtls_addr);
if (vpninfo->csd_scriptname) {
......
......@@ -767,6 +767,7 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr,
mactype = "unknown";
vpn_progress(vpninfo, PRG_DEBUG, _("ESP HMAC: 0x%02x (%s)\n"),
data[0], mactype);
vpninfo->esp_hmac = data[0];
break;
}
......@@ -783,45 +784,52 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr,
enctype = "unknown";
vpn_progress(vpninfo, PRG_DEBUG, _("ESP encryption: 0x%02x (%s)\n"),
data[0], enctype);
vpninfo->esp_enc = data[0];
break;
}
case GRP_ATTR(8, 3):
if (attrlen != 1)
goto badlen;
vpninfo->esp_compr = data[0];
vpn_progress(vpninfo, PRG_DEBUG, _("ESP compression: %d\n"), data[0]);
break;
case GRP_ATTR(8, 4):
if (attrlen != 2)
goto badlen;
vpn_progress(vpninfo, PRG_DEBUG, _("ESP port: %d\n"), TLV_BE16(data));
vpninfo->esp_port = TLV_BE16(data);
vpn_progress(vpninfo, PRG_DEBUG, _("ESP port: %d\n"), vpninfo->esp_port);
break;
case GRP_ATTR(8, 5):
if (attrlen != 4)
goto badlen;
vpn_progress(vpninfo, PRG_DEBUG, _("ESP key lifetime: %d bytes\n"),
TLV_BE32(data));
vpninfo->esp_lifetime_bytes = TLV_BE32(data);
vpn_progress(vpninfo, PRG_DEBUG, _("ESP key lifetime: %u bytes\n"),
vpninfo->esp_lifetime_bytes);
break;
case GRP_ATTR(8, 6):
if (attrlen != 4)
goto badlen;
vpn_progress(vpninfo, PRG_DEBUG, _("ESP key lifetime: %d seconds\n"),
TLV_BE32(data));
vpninfo->esp_lifetime_seconds = TLV_BE32(data);
vpn_progress(vpninfo, PRG_DEBUG, _("ESP key lifetime: %u seconds\n"),
vpninfo->esp_lifetime_seconds);
break;
case GRP_ATTR(8, 9):
if (attrlen != 4)
goto badlen;
vpn_progress(vpninfo, PRG_DEBUG, _("ESP to SSL fallback: %d seconds\n"),
TLV_BE32(data));
vpninfo->esp_ssl_fallback = TLV_BE32(data);
vpn_progress(vpninfo, PRG_DEBUG, _("ESP to SSL fallback: %u seconds\n"),
vpninfo->esp_ssl_fallback);
break;
case GRP_ATTR(8, 10):
if (attrlen != 4)
goto badlen;
vpninfo->esp_replay_protect = TLV_BE32(data);
vpn_progress(vpninfo, PRG_DEBUG, _("ESP replay protection: %d\n"),
TLV_BE32(data));
break;
......@@ -829,11 +837,15 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr,
case GRP_ATTR(7, 1):
if (attrlen != 4)
goto badlen;
memcpy(vpninfo->esp_out.spi, data, 4);
vpn_progress(vpninfo, PRG_DEBUG, _("ESP SPI (outbound): %d\n"),
TLV_BE32(data));
break;
case GRP_ATTR(7, 2):
if (attrlen != 0x40)
goto badlen;
memcpy(vpninfo->esp_out.secrets, data, 0x40);
vpn_progress(vpninfo, PRG_DEBUG, _("%d bytes of ESP secrets\n"),
attrlen);
break;
......@@ -880,6 +892,21 @@ static const unsigned char kmp_tail_out[] = { 0x01, 0x00, 0x00, 0x00, 0x01,
static const unsigned char data_hdr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x2c, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const unsigned char esp_kmp_hdr[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2e,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* KMP header */
0x00, 0x56, /* KMP length */
0x00, 0x07, 0x00, 0x00, 0x00, 0x50, /* TLV group 7 */
0x00, 0x01, 0x00, 0x00, 0x00, 0x04, /* Attr 1 (SPI) */
};
/* Followed by 4 bytes of SPI */
static const unsigned char esp_kmp_part2[] = {
0x00, 0x02, 0x00, 0x00, 0x00, 0x40, /* Attr 2 (secrets) */
};
/* And now 0x40 bytes of random secret for encryption and HMAC key */
int oncp_connect(struct openconnect_info *vpninfo)
{
int ret, ofs, kmp, kmpend, kmplen, attr, attrlen, group, grouplen, groupend;
......@@ -1094,6 +1121,7 @@ int oncp_connect(struct openconnect_info *vpninfo)
group = reqbuf->pos;
buf_append_tlv_be32(reqbuf, 2, vpninfo->ip_info.mtu);
if (buf_error(reqbuf)) {
enomem:
vpn_progress(vpninfo, PRG_ERR,
_("Error creating oNCP negotiation request\n"));
ret = buf_error(reqbuf);
......@@ -1101,6 +1129,19 @@ int oncp_connect(struct openconnect_info *vpninfo)
}
put_len32(reqbuf, group);
put_len16(reqbuf, kmp);
if (!setup_esp_keys(vpninfo)) {
/* Since we'll want to do this in the oncp_mainloop too, where it's easier
* *not* to have an oc_text_buf and build it up manually, and since it's
* all fixed size and fairly simple anyway, just hard-code the packet */
buf_append_bytes(reqbuf, esp_kmp_hdr, sizeof(esp_kmp_hdr));
buf_append_bytes(reqbuf, vpninfo->esp_in.spi, sizeof(vpninfo->esp_in.spi));
buf_append_bytes(reqbuf, esp_kmp_part2, sizeof(esp_kmp_part2));
buf_append_bytes(reqbuf, vpninfo->esp_in.secrets, sizeof(vpninfo->esp_in.secrets));
if (buf_error(reqbuf))
goto enomem;
}
/* Length at the start of the packet is little-endian */
reqbuf->data[0] = (reqbuf->pos - 2);
reqbuf->data[1] = (reqbuf->pos - 2) >> 8;
......
......@@ -56,6 +56,7 @@
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#include <gnutls/x509.h>
#include <gnutls/crypto.h>
#ifdef HAVE_TROUSERS
#include <trousers/tss.h>
#include <trousers/trousers.h>
......@@ -236,6 +237,17 @@ struct vpn_proto {
void (*udp_shutdown)(struct openconnect_info *vpninfo);
};
struct esp {
#ifdef OPENCONNECT_GNUTLS
gnutls_cipher_hd_t cipher;
gnutls_hmac_hd_t hmac;
#else
#error No OpenSSL support for ESP yet
#endif
unsigned char spi[4];
unsigned char secrets[0x40];
};
struct openconnect_info {
struct vpn_proto proto;
......@@ -246,6 +258,17 @@ struct openconnect_info {
char *redirect_url;
int redirect_type;
unsigned char esp_hmac;
unsigned char esp_enc;
unsigned char esp_compr;
uint32_t esp_replay_protect;
uint32_t esp_lifetime_bytes;
uint32_t esp_lifetime_seconds;
uint32_t esp_ssl_fallback;
struct esp esp_in;
struct esp esp_out;
int esp_port;
int tncc_fd; /* For Juniper TNCC */
const char *csd_xmltag;
int csd_nostub;
......@@ -725,6 +748,10 @@ void openconnect_clear_cookies(struct openconnect_info *vpninfo);
int load_pkcs11_key(struct openconnect_info *vpninfo);
int load_pkcs11_certificate(struct openconnect_info *vpninfo);
/* gnutls-esp.c */
int setup_esp_keys(struct openconnect_info *vpninfo);
void destroy_esp_ciphers(struct esp *esp);
/* {gnutls,openssl}.c */
int ssl_nonblock_read(struct openconnect_info *vpninfo, void *buf, int maxlen);
int ssl_nonblock_write(struct openconnect_info *vpninfo, void *buf, int buflen);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment