]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Add HMAC-SHA256-128 support for ESP
authorDavid Woodhouse <dwmw2@infradead.org>
Sat, 8 Jun 2019 15:20:37 +0000 (16:20 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Sun, 9 Jun 2019 19:47:02 +0000 (20:47 +0100)
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
esp.c
gnutls-esp.c
openconnect-internal.h
openssl-esp.c
pulse.c

diff --git a/esp.c b/esp.c
index 1b907435b56f13275685d75b55d8636b4ffed688..a5cbba7f9420e4933ec3d27e8b21fc87610532bd 100644 (file)
--- a/esp.c
+++ b/esp.c
@@ -34,22 +34,25 @@ int print_esp_keys(struct openconnect_info *vpninfo, const char *name, struct es
        char enckey[256], mackey[256];
 
        switch(vpninfo->esp_enc) {
-       case 0x02:
+       case ENC_AES_128_CBC:
                enctype = "AES-128-CBC (RFC3602)";
                break;
-       case 0x05:
+       case ENC_AES_256_CBC:
                enctype = "AES-256-CBC (RFC3602)";
                break;
        default:
                return -EINVAL;
        }
        switch(vpninfo->esp_hmac) {
-       case 0x01:
+       case HMAC_MD5:
                mactype = "HMAC-MD5-96 (RFC2403)";
                break;
-       case 0x02:
+       case HMAC_SHA1:
                mactype = "HMAC-SHA-1-96 (RFC2404)";
                break;
+       case HMAC_SHA256:
+               mactype = "HMAC-SHA-256-128 (RFC4868)";
+               break;
        default:
                return -EINVAL;
        }
@@ -140,10 +143,10 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                work_done = 1;
 
                /* both supported algos (SHA1 and MD5) have 12-byte MAC lengths (RFC2403 and RFC2404) */
-               if (len <= sizeof(pkt->esp) + 12)
+               if (len <= sizeof(pkt->esp) + vpninfo->hmac_out_len)
                        continue;
 
-               len -= sizeof(pkt->esp) + 12;
+               len -= sizeof(pkt->esp) + vpninfo->hmac_out_len;
                pkt->len = len;
 
                if (pkt->esp.spi == esp->spi) {
@@ -358,6 +361,11 @@ int openconnect_setup_esp_keys(struct openconnect_info *vpninfo, int new_keys)
        if (!vpninfo->dtls_addr)
                return -EINVAL;
 
+       if (vpninfo->esp_hmac == HMAC_SHA256)
+               vpninfo->hmac_out_len = 16;
+       else /* MD5 and SHA1 */
+               vpninfo->hmac_out_len = 12;
+
        if (new_keys) {
                vpninfo->old_esp_maxseq = vpninfo->esp_in[vpninfo->current_esp_in].seq + 32;
                vpninfo->current_esp_in ^= 1;
@@ -382,7 +390,7 @@ int openconnect_setup_esp_keys(struct openconnect_info *vpninfo, int new_keys)
        }
 
        /* This is the minimum; some implementations may increase it */
-       vpninfo->pkt_trailer = 17 + 16 + 20; /* 17 for pad, 16 for IV, 20 for HMAC (of which we send 12) */
+       vpninfo->pkt_trailer = MAX_ESP_PAD + MAX_IV_SIZE + MAX_HMAC_SIZE;
 
        vpninfo->esp_out.seq = vpninfo->esp_out.seq_backlog = 0;
        esp_in->seq = esp_in->seq_backlog = 0;
index 08adc11501167b15bddd4548a8af4bca2bb05230..fbaeae2f1a80945326f8ad8b769c5be683269a8a 100644 (file)
@@ -94,6 +94,9 @@ int init_esp_ciphers(struct openconnect_info *vpninfo, struct esp *esp_out, stru
        case HMAC_SHA1:
                macalg = GNUTLS_MAC_SHA1;
                break;
+       case HMAC_SHA256:
+               macalg = GNUTLS_MAC_SHA256;
+               break;
        default:
                return -EINVAL;
        }
@@ -116,7 +119,7 @@ int init_esp_ciphers(struct openconnect_info *vpninfo, struct esp *esp_out, stru
 /* pkt->len shall be the *payload* length. Omitting the header and the 12-byte HMAC */
 int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct pkt *pkt)
 {
-       unsigned char hmac_buf[20];
+       unsigned char hmac_buf[MAX_HMAC_SIZE];
        int err;
 
        err = gnutls_hmac(esp->hmac, &pkt->esp, sizeof(pkt->esp) + pkt->len);
@@ -127,7 +130,7 @@ int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct
                return -EIO;
        }
        gnutls_hmac_output(esp->hmac, hmac_buf);
-       if (memcmp(hmac_buf, pkt->data + pkt->len, 12)) {
+       if (memcmp(hmac_buf, pkt->data + pkt->len, vpninfo->hmac_out_len)) {
                vpn_progress(vpninfo, PRG_DEBUG,
                             _("Received ESP packet with invalid HMAC\n"));
                return -EINVAL;
@@ -186,5 +189,5 @@ int encrypt_esp_packet(struct openconnect_info *vpninfo, struct pkt *pkt)
 
        memcpy(vpninfo->esp_out.iv, pkt->data + pkt->len + padlen + 2, blksize);
        gnutls_cipher_encrypt(vpninfo->esp_out.cipher, vpninfo->esp_out.iv, blksize);
-       return sizeof(pkt->esp) + pkt->len + padlen + 2 + 12;
+       return sizeof(pkt->esp) + pkt->len + padlen + 2 + vpninfo->hmac_out_len;
 }
index 75d93a79f1cba4dbe9bd080627ddd148e5a5cd3a..a942a7ec4ca2611745715b6f18104c34c174ec98 100644 (file)
@@ -381,6 +381,7 @@ struct openconnect_info {
        struct esp esp_out;
        int enc_key_len;
        int hmac_key_len;
+       int hmac_out_len;
        uint32_t esp_magic;  /* GlobalProtect magic ping address (network-endian) */
 
        int tncc_fd; /* For Juniper TNCC */
@@ -713,11 +714,17 @@ struct openconnect_info {
 #define AC_PKT_COMPRESSED      8       /* Compressed data */
 #define AC_PKT_TERM_SERVER     9       /* Server kick */
 
-/* Encryption and HMAC algorithms (matching Juniper's binary encoding) */
+/* Encryption and HMAC algorithms (matching Juniper/Pulse binary encoding) */
 #define ENC_AES_128_CBC                2
 #define ENC_AES_256_CBC                5
+
 #define HMAC_MD5               1
 #define HMAC_SHA1              2
+#define HMAC_SHA256            3
+
+#define MAX_HMAC_SIZE          32      /* SHA256 */
+#define MAX_IV_SIZE            16
+#define MAX_ESP_PAD            17      /* Including the next-header field */
 
 #define vpn_progress(_v, lvl, ...) do {                                        \
        if ((_v)->verbose >= (lvl))                                     \
index 364bb283161bbcf967667be07451f392157ef84b..f03a033ec0af2877bc2c1c0005c04f63be378d2e 100644 (file)
@@ -130,6 +130,9 @@ int init_esp_ciphers(struct openconnect_info *vpninfo, struct esp *esp_out, stru
        case HMAC_SHA1:
                macalg = EVP_sha1();
                break;
+       case HMAC_SHA256:
+               macalg = EVP_sha256();
+               break;
        default:
                return -EINVAL;
        }
@@ -150,7 +153,7 @@ int init_esp_ciphers(struct openconnect_info *vpninfo, struct esp *esp_out, stru
 /* pkt->len shall be the *payload* length. Omitting the header and the 12-byte HMAC */
 int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct pkt *pkt)
 {
-       unsigned char hmac_buf[20];
+       unsigned char hmac_buf[MAX_HMAC_SIZE];
        unsigned int hmac_len = sizeof(hmac_buf);
        int crypt_len = pkt->len;
 
@@ -158,7 +161,7 @@ int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct
        HMAC_Update(esp->hmac, (void *)&pkt->esp, sizeof(pkt->esp) + pkt->len);
        HMAC_Final(esp->hmac, hmac_buf, &hmac_len);
 
-       if (memcmp(hmac_buf, pkt->data + pkt->len, 12)) {
+       if (memcmp(hmac_buf, pkt->data + pkt->len, vpninfo->hmac_out_len)) {
                vpn_progress(vpninfo, PRG_DEBUG,
                             _("Received ESP packet with invalid HMAC\n"));
                return -EINVAL;
@@ -190,7 +193,7 @@ int encrypt_esp_packet(struct openconnect_info *vpninfo, struct pkt *pkt)
 {
        int i, padlen;
        int blksize = 16;
-       unsigned int hmac_len = 20;
+       unsigned int hmac_len = vpninfo->hmac_out_len;
        int crypt_len;
 
        /* This gets much more fun if the IV is variable-length */
@@ -220,5 +223,5 @@ int encrypt_esp_packet(struct openconnect_info *vpninfo, struct pkt *pkt)
 
        EVP_EncryptUpdate(vpninfo->esp_out.cipher, vpninfo->esp_out.iv, &blksize,
                          pkt->data + crypt_len + hmac_len - blksize, blksize);
-       return sizeof(pkt->esp) + crypt_len + 12;
+       return sizeof(pkt->esp) + crypt_len + vpninfo->hmac_out_len;
 }
diff --git a/pulse.c b/pulse.c
index fbd351b45a7115c5c851f9c19f50520b9cf5a617..8dd9cbd0b6a470ab9a80d6045ebd8d1cf0f1dce2 100644 (file)
--- a/pulse.c
+++ b/pulse.c
@@ -378,6 +378,9 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type,
                } else if (val == HMAC_SHA1) {
                        mactype = "SHA1";
                        vpninfo->hmac_key_len = 20;
+               } else if (val == HMAC_SHA256) {
+                       mactype = "SHA256";
+                       vpninfo->hmac_key_len = 32;
                } else
                        mactype = "unknown";
                vpn_progress(vpninfo, PRG_DEBUG, _("ESP HMAC: 0x%04x (%s)\n"),
@@ -1049,8 +1052,10 @@ static int pulse_authenticate(struct openconnect_info *vpninfo, int connecting)
        /* IF-T version request. */
        buf_truncate(reqbuf);
        buf_append_ift_hdr(reqbuf, VENDOR_TCG, IFT_VERSION_REQUEST);
-       /* min=1, max=1, preferred version=1 */
-       buf_append_be32(reqbuf, 0x00010101);
+       /* Min version 1, max 2, preferred 2. Not that we actually do v2; the auth is
+        * still all IF-T/TLS v1. But the server won't offer us HMAC-SHA256 unless we
+        * advertise v2 */
+       buf_append_be32(reqbuf, 0x00010202);
        ret = send_ift_packet(vpninfo, reqbuf);
        if (ret)
                goto out;