From 9f708d88f753d612122612cfa60f9d5f6a2e099d Mon Sep 17 00:00:00 2001 From: Elias Norberg Date: Mon, 25 Nov 2019 22:30:55 +0100 Subject: [PATCH 1/2] Initial Pulse TNCC support --- pulse.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 217 insertions(+), 13 deletions(-) diff --git a/pulse.c b/pulse.c index 6026fd2e..4711d272 100644 --- a/pulse.c +++ b/pulse.c @@ -143,24 +143,52 @@ static void buf_fill_eap_len(struct oc_text_buf *buf, int ofs) store_be16(buf->data + ofs + 2, buf->pos - ofs); } +static void buf_append_avp_with_flags(struct oc_text_buf *buf, uint32_t type, uint16_t flags, const void *bytes, int len) +{ + buf_append_be32(buf, type); + buf_append_be16(buf, flags); + buf_append_be16(buf, len + 12); + buf_append_be32(buf, VENDOR_JUNIPER2); + buf_append_bytes(buf, bytes, len); + if (len & 3) { + uint32_t pad = 0; + buf_append_bytes(buf, &pad, 4 - ( len & 3 )); + } +} + static void buf_append_avp(struct oc_text_buf *buf, uint32_t type, const void *bytes, int len) { + buf_append_avp_with_flags(buf, type, 0x8000, bytes, len); +} + +static void buf_append_avp_string(struct oc_text_buf *buf, uint32_t type, const char *str) +{ + buf_append_avp(buf, type, str, strlen(str)); +} + +static void buf_append_avp_string_flags(struct oc_text_buf *buf, uint32_t type, uint16_t flags, const char *str) { + buf_append_avp_with_flags(buf, type, flags, str, strlen(str)); +} + +// buf_append_avp_string_subtype appends a AVP string with JUNIPER subtype added +static void buf_append_avp_string_subtype(struct oc_text_buf *buf, uint32_t type, uint32_t subvendor, uint8_t subtype, const char *str, int len) +{ + if (len == -1) { + len = strlen(str); + } + buf_append_be32(buf, type); - buf_append_be16(buf, 0x8000); - buf_append_be16(buf, len + 12); + buf_append_be16(buf, 0xC000); + buf_append_be16(buf, len + 16); buf_append_be32(buf, VENDOR_JUNIPER2); - buf_append_bytes(buf, bytes, len); + buf_append_be32(buf, (subvendor << 8) | subtype); + buf_append_bytes(buf, str, len); if (len & 3) { uint32_t pad = 0; buf_append_bytes(buf, &pad, 4 - ( len & 3 )); } } -static void buf_append_avp_string(struct oc_text_buf *buf, uint32_t type, const char *str) -{ - buf_append_avp(buf, type, str, strlen(str)); -} - static void buf_append_avp_be32(struct oc_text_buf *buf, uint32_t type, uint32_t val) { uint32_t val_be; @@ -877,6 +905,150 @@ static int pulse_request_realm_choice(struct openconnect_info *vpninfo, struct o return ret; } +static int pulse_request_tncc_reply(struct openconnect_info *vpninfo, struct oc_text_buf *buf) +{ + const char *message = "" + ""; + const char *acceptLanguage = "Accept-Language: en-US"; + + struct oc_text_buf *reqbuf = buf_alloc(); + struct oc_text_buf *encapsulation1 = buf_alloc(); + struct oc_text_buf *encapsulation2 = buf_alloc(); + char encapsulationBytes[9]; + + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER, 0x18, message, -1); + + buf_append_avp_with_flags(encapsulation1, 0x0ce4, 0xc000, reqbuf->data, reqbuf->pos); + buf_append_avp_string_flags(encapsulation1, 0xce5, 0xc000, acceptLanguage); + buf_append_avp(encapsulation1, 0xcf3, (unsigned char*)"\x00\x00\x00\x01", 4); + + // Encapsulate in second encapsulation packet + store_be32(encapsulationBytes, EXPANDED_JUNIPER); + store_be32(encapsulationBytes+4, 0x03); + encapsulationBytes[8] = 0x01; + + buf_append_bytes(encapsulation2, encapsulationBytes, 9); + buf_append_avp_with_flags(encapsulation2, 0x13, 0xc000, encapsulation1->data, encapsulation1->pos); + buf_free(encapsulation1); + + // Build AVP packet with specific settings + buf_append_be32(buf, 0x4f); + buf_append_be16(buf, 0x4000); + // Total length of data, plus length of this packet, plus length of additional contents + buf_append_be16(buf, encapsulation2->pos + 12); + buf_append_be32(buf, 0x020202a9); // Vendor + + buf_append_bytes(buf, encapsulation2->data, encapsulation2->pos); + + // Add final padding + if (encapsulation2->pos & 3) { + // These where added instead of normal padding? + const char *pad = "\x2e\x38\x39"; + buf_append_bytes(buf, pad, 4 - (encapsulation2->pos & 3 )); + } + + return 0; +} + +static int pulse_request_tncc_info(struct openconnect_info *vpninfo, struct oc_text_buf *buf) +{ + const char *message = "{\"messageType\":0,\"clientType\":1,\"message\":[]}"; + const char *parameters = ""; + const char data[] = { + 0x00, 0x07, 0x00, 0x96, 0x00, 0x00, 0x01, 0x37, + 0x00, 0x02, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x1e, + 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x04, 0x00, 0x01, 0x37, 0x00, 0x00, 0x07, + 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x01, 0x37, 0x80, 0x00, 0x07, 0x00, + 0x01, 0x00, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x01, + 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x08, 0x00, 0x01, 0x02, 0x00, 0x0b, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, + 0x01, 0x03, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01}; + const char *funkMessage = " "; + const char *attributes = ""; + const char *macaddr = "set macaddr;00:00:00:00:00:00"; + const char *policyRequest = "policy request\x00v7\x00"; + const char *acceptLanguage = "Accept-Language: en-US"; + + // We have multiple buffers, because there are multiple levels of encapsulation + struct oc_text_buf *reqbuf = buf_alloc(); + struct oc_text_buf *encapsulation1 = buf_alloc(); + struct oc_text_buf *encapsulation2 = buf_alloc(); + char encapsulationBytes[9]; + + // Build inner packet (flags 0xc0) + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER, 0x1b, message, -1); + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER, 0x18, parameters, -1); + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER, 0x17, data, 154); + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER, 0x01, funkMessage, strlen(funkMessage)+1); // funkMessage should include null-terminator + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER2, 0x01, attributes, strlen(attributes)+1); // attributes should include null-terminator + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER2, 0x16, macaddr, -1); + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER2, 0x16, policyRequest, 18); + + // Encapsulate inner packet + buf_append_avp_with_flags(encapsulation1, 0x0ce4, 0xc000, reqbuf->data, reqbuf->pos); + buf_append_avp_string_flags(encapsulation1, 0xce5, 0xc000, acceptLanguage); + buf_append_avp(encapsulation1, 0xcf3, (unsigned char*)"\x00\x00\x00\x01", 4); + buf_free(reqbuf); + + // Encapsulate in second encapsulation packet + store_be32(encapsulationBytes, EXPANDED_JUNIPER); + store_be32(encapsulationBytes+4, 0x03); + encapsulationBytes[8] = 0x01; + buf_append_bytes(encapsulation2, encapsulationBytes, 9); + buf_append_avp_with_flags(encapsulation2, 0x13, 0xc000, encapsulation1->data, encapsulation1->pos); + buf_free(encapsulation1); + + // Add local hostname + buf_append_avp_string(buf,0x0d6d, vpninfo->localname); + + // Build AVP packet with specific settings + buf_append_be32(buf, 0x4f); + buf_append_be16(buf, 0x4000); + // Total length of data, plus length of this packet, plus length of additional contents + buf_append_be16(buf, encapsulation2->pos + 12); + buf_append_be32(buf, 0x02010911); // Vendor + + buf_append_bytes(buf, encapsulation2->data, encapsulation2->pos); + + // Add final padding + if (encapsulation2->pos & 3) { + uint32_t pad = 0; + buf_append_bytes(buf, &pad, 4 - (encapsulation2->pos & 3 )); + } + + buf_free(encapsulation2); + return 0; +} + static int pulse_request_session_kill(struct openconnect_info *vpninfo, struct oc_text_buf *reqbuf, int sessions, unsigned char *eap) { @@ -1260,8 +1432,8 @@ static int pulse_authenticate(struct openconnect_info *vpninfo, int connecting) void *avp_p, *p; unsigned char *eap; int cookie_found = 0; - int j2_found = 0, realms_found = 0, realm_entry = 0, old_sessions = 0, gtc_found = 0; - uint8_t j2_code = 0; + int j2_found = 0, realms_found = 0, realm_entry = 0, old_sessions = 0, gtc_found = 0, tncc_found = 0; + uint8_t j2_code = 0, tncc_code = 0; void *ttls = NULL; char *user_prompt = NULL, *pass_prompt = NULL, *gtc_prompt = NULL, *signin_prompt = NULL; char *user2_prompt = NULL, *pass2_prompt = NULL; @@ -1550,7 +1722,7 @@ static int pulse_authenticate(struct openconnect_info *vpninfo, int connecting) else prompt_flags &= ~PROMPT_GTC_NEXT; - realm_entry = realms_found = j2_found = old_sessions = 0, gtc_found = 0; + realm_entry = realms_found = j2_found = old_sessions = 0, gtc_found = tncc_found = 0; eap = recv_eap_packet(vpninfo, ttls, (void *)bytes, sizeof(bytes)); if (!eap) { ret = -EIO; @@ -1663,13 +1835,35 @@ static int pulse_authenticate(struct openconnect_info *vpninfo, int connecting) gtc_found = 1; free(gtc_prompt); gtc_prompt = strndup(avp_c + 5, avp_len - 5); - } else if (avp_len == 13 && load_be32(avp_c + 4) == EXPANDED_JUNIPER) { + } else if (load_be32(avp_c + 4) == EXPANDED_JUNIPER) { switch (load_be32(avp_c + 8)) { case 2: /* Expanded Juniper/2: password */ + if (avp_len != 13) { // Password requests are expected to be 13 bytes long + goto auth_unknown; + } j2_found = 1; j2_code = avp_c[12]; break; + case 3: /* Host checker requested */ + // We currently only know how to handle type 3 packets with code set to 21 + if (avp_c[12] == 0x21) { + if (avp_len != 13) { // TNCC info requests are expected to be 13 bytes long + goto auth_unknown; + } + + tncc_found = 1; + tncc_code = avp_c[12]; + break; + } + + if (avp_c[12] == 0x01) { + tncc_found = 1; + tncc_code = avp_c[12]; + break; + } + + goto auth_unknown; default: goto auth_unknown; } @@ -1682,7 +1876,7 @@ static int pulse_authenticate(struct openconnect_info *vpninfo, int connecting) } /* We want it to be precisely one type of request, not a mixture. */ - if (realm_entry + !!realms_found + j2_found + gtc_found + cookie_found + !!old_sessions != 1 && + if (realm_entry + !!realms_found + j2_found + gtc_found + tncc_found + cookie_found + !!old_sessions != 1 && !signin_prompt) { auth_unknown: vpn_progress(vpninfo, PRG_ERR, @@ -1730,6 +1924,16 @@ static int pulse_authenticate(struct openconnect_info *vpninfo, int connecting) (prompt_flags & PROMPT_PRIMARY) ? pass_prompt : pass2_prompt); if (ret) goto out; + } else if (tncc_found) { + vpn_progress(vpninfo, PRG_TRACE, ("Pulse TNCC request\n")); + if (tncc_code == 0x21) { + ret = pulse_request_tncc_info(vpninfo, reqbuf); + } else if (tncc_code == 0x01) { + ret = pulse_request_tncc_reply(vpninfo, reqbuf); + } + + if (ret) + goto out; } else if (gtc_found) { vpn_progress(vpninfo, PRG_TRACE, _("Pulse password general token code request\n")); -- GitLab From 2327d83343bfc323eeb4d3f0360e3fdba3109b44 Mon Sep 17 00:00:00 2001 From: Jonas Sjoquist Date: Wed, 11 Dec 2019 14:14:54 +0100 Subject: [PATCH 2/2] Set data_file_details to current time In order to avoid 'Insufficient configuration found' error due to old date. --- pulse.c | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/pulse.c b/pulse.c index 4711d272..e663976b 100644 --- a/pulse.c +++ b/pulse.c @@ -905,27 +905,34 @@ static int pulse_request_realm_choice(struct openconnect_info *vpninfo, struct o return ret; } +#define MSG "" \ + "" + +// MSG size + size of time +#define MSG_SIZE (sizeof(MSG) + 11) + static int pulse_request_tncc_reply(struct openconnect_info *vpninfo, struct oc_text_buf *buf) { - const char *message = "" - ""; + char message[MSG_SIZE]; + time_t rawtime; + struct tm *info; const char *acceptLanguage = "Accept-Language: en-US"; struct oc_text_buf *reqbuf = buf_alloc(); @@ -933,6 +940,10 @@ static int pulse_request_tncc_reply(struct openconnect_info *vpninfo, struct oc_ struct oc_text_buf *encapsulation2 = buf_alloc(); char encapsulationBytes[9]; + time(&rawtime); + info = localtime(&rawtime); + strftime(message,MSG_SIZE,MSG, info); + buf_append_avp_string_subtype(reqbuf, 0xce7, VENDOR_JUNIPER, 0x18, message, -1); buf_append_avp_with_flags(encapsulation1, 0x0ce4, 0xc000, reqbuf->data, reqbuf->pos); -- GitLab