>From b0d2fe37ca86bbb476efd12bd4bf0cf2719a55dc Mon Sep 17 00:00:00 2001 From: Phil Mayers Date: Mon, 11 Oct 2010 14:35:35 +0100 Subject: [PATCH 2/4] move soh code into server, so we can see REQUEST/RDEBUG --- src/lib/soh.c | 579 -------------------------------------------------------- src/main/soh.c | 579 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 579 insertions(+), 579 deletions(-) delete mode 100644 src/lib/soh.c create mode 100644 src/main/soh.c diff --git a/src/lib/soh.c b/src/lib/soh.c deleted file mode 100644 index 3310c36..0000000 --- a/src/lib/soh.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * soh.c contains the interfaces that are called from eap - * - * Version: $Id$ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * Copyright 2010 Phil Mayers - */ - -#include -RCSID("$Id$") - -#include - -/* - * This code implements parsing of MS-SOH data into FreeRadius AVPs - * allowing for FreeRadius MS-NAP policies - */ - -typedef struct { - uint16_t tlv_type; /* ==7 */ - uint16_t tlv_len; - uint32_t tlv_vendor; - - /* then it's either an soh request or response */ - uint16_t soh_type; /* ==2 for request, 1 for response */ - uint16_t soh_len; - - /* an soh-response may now follow... */ -} eap_soh; - -typedef struct { - uint16_t outer_type; - uint16_t outer_len; - uint32_t vendor; - uint16_t inner_type; - uint16_t inner_len; -} soh_response; - -typedef struct { - uint16_t outer_type; - uint16_t outer_len; - uint32_t vendor; - uint8_t corrid[24]; - uint8_t intent; - uint8_t content_type; -} soh_mode_subheader; - -typedef struct { - uint16_t tlv_type; - uint16_t tlv_len; -} soh_tlv; - -/* utils for pulling big-endian 2/3/4 byte integers - * caller must ensure enough data exists at "p" - */ -uint16_t soh_pull_be_16(const uint8_t *p) { - uint16_t r; - - r = *p++ << 8; - r += *p++; - - return r; -} -uint32_t soh_pull_be_24(const uint8_t *p) { - uint32_t r; - - r = *p++ << 16; - r += *p++ << 8; - r += *p++; - - return r; -} -uint32_t soh_pull_be_32(const uint8_t *p) { - uint32_t r; - - r = *p++ << 24; - r += *p++ << 16; - r += *p++ << 8; - r += *p++; - - return r; -} - -/* - * This parses the microsoft type/value (note: NOT type/length/value) data; see - * section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip - * unknown types; we need to know their length ahead of time. Therefore, we abort - * if we find an unknown type. - */ -static int eapsoh_mstlv(VALUE_PAIR *sohvp, const uint8_t *p, unsigned int data_len) { - VALUE_PAIR *vp; - uint8_t c; - int t; - - while (data_len > 0) { - c = *p++; - data_len--; - - switch (c) { - case 1: - /* MS-Machine-Inventory-Packet - * MS-SOH section 2.2.4.1 - */ - if (data_len < 18) { - DEBUG("insufficient data for MS-Machine-Inventory-Packet\n"); - return 0; - } - data_len -= 18; - - vp = pairmake("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ); - pairadd(&sohvp, vp); - - vp = pairmake("SoH-MS-Machine-OS-version", NULL, T_OP_EQ); - vp->vp_integer = soh_pull_be_32(p); p+=4; - pairadd(&sohvp, vp); - - vp = pairmake("SoH-MS-Machine-OS-release", NULL, T_OP_EQ); - vp->vp_integer = soh_pull_be_32(p); p+=4; - pairadd(&sohvp, vp); - - vp = pairmake("SoH-MS-Machine-OS-build", NULL, T_OP_EQ); - vp->vp_integer = soh_pull_be_32(p); p+=4; - pairadd(&sohvp, vp); - - vp = pairmake("SoH-MS-Machine-SP-version", NULL, T_OP_EQ); - vp->vp_integer = soh_pull_be_16(p); p+=2; - pairadd(&sohvp, vp); - - vp = pairmake("SoH-MS-Machine-SP-release", NULL, T_OP_EQ); - vp->vp_integer = soh_pull_be_16(p); p+=2; - pairadd(&sohvp, vp); - - vp = pairmake("SoH-MS-Machine-Processor", NULL, T_OP_EQ); - vp->vp_integer = soh_pull_be_16(p); p+=2; - pairadd(&sohvp, vp); - - break; - - case 2: - /* MS-Quarantine-State - FIXME: currently unhandled - * MS-SOH 2.2.4.1 - * - * 1 byte reserved - * 1 byte flags - * 8 bytes NT Time field (100-nanosec since 1 Jan 1601) - * 2 byte urilen - * N bytes uri - */ - p += 10; - t = soh_pull_be_16(p); /* t == uri len */ - p += 2; - p += t; - data_len -= 12 + t; - break; - - case 3: - /* MS-Packet-Info - * MS-SOH 2.2.4.3 - */ - DEBUG("SoH MS-Packet-Info %s vers=%i\n", *p & 0x10 ? "request" : "response", *p & 0xf); - p++; - data_len--; - break; - - case 4: - /* MS-SystemGenerated-Ids - FIXME: currently unhandled - * MS-SOH 2.2.4.4 - * - * 2 byte length - * N bytes (3 bytes IANA enterprise# + 1 byte component id#) - */ - t = soh_pull_be_16(p); - p += 2; - p += t; - data_len -= 2 + t; - break; - - case 5: - /* MS-MachineName - * MS-SOH 2.2.4.5 - * - * 1 byte namelen - * N bytes name - */ - t = soh_pull_be_16(p); - p += 2; - - vp = pairmake("SoH-MS-Machine-Name", NULL, T_OP_EQ); - memcpy(vp->vp_strvalue, p, t); - vp->vp_strvalue[t] = 0; - - pairadd(&sohvp, vp); - p += t; - data_len -= 2 + t; - break; - - case 6: - /* MS-CorrelationId - * MS-SOH 2.2.4.6 - * - * 24 bytes opaque binary which we might, in future, have - * to echo back to the client in a final SoHR - */ - vp = pairmake("SoH-MS-Correlation-Id", NULL, T_OP_EQ); - memcpy(vp->vp_octets, p, 24); - vp->length = 24; - pairadd(&sohvp, vp); - p += 24; - data_len -= 24; - break; - - case 7: - /* MS-Installed-Shvs - FIXME: currently unhandled - * MS-SOH 2.2.4.7 - * - * 2 bytes length - * N bytes (3 bytes IANA enterprise# + 1 byte component id#) - */ - t = soh_pull_be_16(p); - p += 2; - p += t; - data_len -= 2 + t; - break; - - case 8: - /* MS-Machine-Inventory-Ex - * MS-SOH 2.2.4.8 - * - * 4 bytes reserved - * 1 byte product type (client=1 domain_controller=2 server=3) - */ - p += 4; - vp = pairmake("SoH-MS-Machine-Role", NULL, T_OP_EQ); - vp->vp_integer = *p; - pairadd(&sohvp, vp); - p++; - data_len -= 5; - break; - - default: - DEBUG("SoH Unknown MS TV %i stopping\n", c); - return 0; - } - } - return 1; -} - -static const char* clientstatus2str(uint32_t hcstatus) { - switch (hcstatus) { - /* this lot should all just be for windows updates */ - case 0xff0005: - return "wua-ok"; - case 0xff0006: - return "wua-missing"; - case 0xff0008: - return "wua-not-started"; - case 0xc0ff000c: - return "wua-no-wsus-server"; - case 0xc0ff000d: - return "wua-no-wsus-clientid"; - case 0xc0ff000e: - return "wua-disabled"; - case 0xc0ff000f: - return "wua-comm-failure"; - - /* these next 3 are for all health-classes */ - case 0xc0ff0002: - return "not-installed"; - case 0xc0ff0003: - return "down"; - case 0xc0ff0018: - return "not-started"; - } - return NULL; -} - -static const char* healthclass2str(uint8_t hc) { - switch (hc) { - case 0: - return "firewall"; - case 1: - return "antivirus"; - case 2: - return "antispyware"; - case 3: - return "updates"; - case 4: - return "security-updates"; - } - return NULL; -} - -int soh_verify(VALUE_PAIR *sohvp, const uint8_t *data, unsigned int data_len) { - - VALUE_PAIR *vp; - eap_soh hdr; - soh_response resp; - soh_mode_subheader mode; - soh_tlv tlv; - int curr_shid=-1, curr_shid_c=-1, curr_hc=-1; - - hdr.tlv_type = soh_pull_be_16(data); data += 2; - hdr.tlv_len = soh_pull_be_16(data); data += 2; - hdr.tlv_vendor = soh_pull_be_32(data); data += 4; - - if (hdr.tlv_type != 7 || hdr.tlv_vendor != 0x137) { - DEBUG("SoH payload is %i %08x not a ms-vendor packet\n", hdr.tlv_type, hdr.tlv_vendor); - return -1; - } - - hdr.soh_type = soh_pull_be_16(data); data += 2; - hdr.soh_len = soh_pull_be_16(data); data += 2; - if (hdr.soh_type != 1) { - DEBUG("SoH tlv %04x is not a response\n", hdr.soh_type); - return -1; - } - - /* FIXME: check for sufficient data */ - resp.outer_type = soh_pull_be_16(data); data += 2; - resp.outer_len = soh_pull_be_16(data); data += 2; - resp.vendor = soh_pull_be_32(data); data += 4; - resp.inner_type = soh_pull_be_16(data); data += 2; - resp.inner_len = soh_pull_be_16(data); data += 2; - - - if (resp.outer_type!=7 || resp.vendor != 0x137) { - DEBUG("SoH response outer type %i/vendor %08x not recognised\n", resp.outer_type, resp.vendor); - return -1; - } - switch (resp.inner_type) { - case 1: - /* no mode sub-header */ - DEBUG("SoH without mode subheader\n"); - break; - case 2: - mode.outer_type = soh_pull_be_16(data); data += 2; - mode.outer_len = soh_pull_be_16(data); data += 2; - mode.vendor = soh_pull_be_32(data); data += 4; - memcpy(mode.corrid, data, 24); data += 24; - mode.intent = data[0]; - mode.content_type = data[1]; - data += 2; - - if (mode.outer_type != 7 || mode.vendor != 0x137 || mode.content_type != 0) { - DEBUG("SoH mode subheader outer type %i/vendor %08x/content type %i invalid\n", mode.outer_type, mode.vendor, mode.content_type); - return -1; - } - DEBUG("SoH with mode subheader\n"); - break; - default: - DEBUG("SoH invalid inner type %i\n", resp.inner_type); - return -1; - } - - /* subtract off the relevant amount of data */ - if (resp.inner_type==2) { - data_len = resp.inner_len - 34; - } else { - data_len = resp.inner_len; - } - - /* TLV - * MS-SOH 2.2.1 - * See also 2.2.3 - * - * 1 bit mandatory - * 1 bit reserved - * 14 bits tlv type - * 2 bytes tlv length - * N bytes payload - * - */ - while (data_len >= 4) { - tlv.tlv_type = soh_pull_be_16(data); data += 2; - tlv.tlv_len = soh_pull_be_16(data); data += 2; - - data_len -= 4; - - switch (tlv.tlv_type) { - case 2: - /* System-Health-Id TLV - * MS-SOH 2.2.3.1 - * - * 3 bytes IANA/SMI vendor code - * 1 byte component (i.e. within vendor, which SoH component - */ - curr_shid = soh_pull_be_24(data); - curr_shid_c = data[3]; - DEBUG("SoH System-Health-ID vendor %08x component=%i\n", curr_shid, curr_shid_c); - break; - - case 7: - /* Vendor-Specific packet - * MS-SOH 2.2.3.3 - * - * 4 bytes vendor, supposedly ignored by NAP - * N bytes payload; for Microsoft component#0 this is the MS TV stuff - */ - if (curr_shid==0x137 && curr_shid_c==0) { - DEBUG("SoH MS type-value payload\n"); - eapsoh_mstlv(sohvp, data + 4, tlv.tlv_len - 4); - } else { - DEBUG("SoH unhandled vendor-specific TLV %08x/component=%i %i bytes payload\n", curr_shid, curr_shid_c, tlv.tlv_len); - } - break; - - case 8: - /* Health-Class - * MS-SOH 2.2.3.5.6 - * - * 1 byte integer - */ - DEBUG("SoH Health-Class %i\n", data[0]); - curr_hc = data[0]; - break; - - case 9: - /* Software-Version - * MS-SOH 2.2.3.5.7 - * - * 1 byte integer - */ - DEBUG("SoH Software-Version %i\n", data[0]); - break; - - case 11: - /* Health-Class status - * MS-SOH 2.2.3.5.9 - * - * variable data; for the MS System Health vendor, these are 4-byte - * integers which are a really, really dumb format: - * - * 28 bits ignore - * 1 bit - 1==product snoozed - * 1 bit - 1==microsoft product - * 1 bit - 1==product up-to-date - * 1 bit - 1==product enabled - */ - DEBUG("SoH Health-Class-Status - current shid=%08x component=%i\n", curr_shid, curr_shid_c); - - if (curr_shid==0x137 && curr_shid_c==128) { - - const char *s, *t; - uint32_t hcstatus = soh_pull_be_32(data); - - DEBUG("SoH Health-Class-Status microsoft DWORD=%08x\n", hcstatus); - - vp = pairmake("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ); - switch (curr_hc) { - case 4: - /* security updates */ - s = "security-updates"; - switch (hcstatus) { - case 0xff0005: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok all-installed", s); - break; - case 0xff0006: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn some-missing", s); - break; - case 0xff0008: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn never-started", s); - break; - case 0xc0ff000c: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error no-wsus-srv", s); - break; - case 0xc0ff000d: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error no-wsus-clid", s); - break; - case 0xc0ff000e: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn wsus-disabled", s); - break; - case 0xc0ff000f: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error comm-failure", s); - break; - case 0xc0ff0010: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn needs-reboot", s); - break; - default: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus); - break; - } - break; - - case 3: - /* auto updates */ - s = "auto-updates"; - switch (hcstatus) { - case 1: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn disabled", s); - break; - case 2: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=check-only", s); - break; - case 3: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=download", s); - break; - case 4: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=install", s); - break; - case 5: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn unconfigured", s); - break; - case 0xc0ff0003: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn service-down", s); - break; - case 0xc0ff0018: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn never-started", s); - break; - default: - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus); - break; - } - break; - - default: - /* other - firewall, antivirus, antispyware */ - s = healthclass2str(curr_hc); - if (s) { - /* bah. this is vile. stupid microsoft - */ - if (hcstatus & 0xff000000) { - /* top octet non-zero means an error - * FIXME: is this always correct? MS-WSH 2.2.8 is unclear - */ - t = clientstatus2str(hcstatus); - if (t) { - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %s", s, t); - } else { - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus); - } - } else { - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), - "%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i", - s, - hcstatus & 0x8 ? 1 : 0, - hcstatus & 0x4 ? 1 : 0, - hcstatus & 0x2 ? 1 : 0, - hcstatus & 0x1 ? 1 : 0 - ); - } - } else { - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%i unknown %08x", curr_hc, hcstatus); - } - break; - } - } else { - vp = pairmake("SoH-Other-Health-Status", NULL, T_OP_EQ); - /* FIXME: what to do with the payload? */ - snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%08x/%i ?", curr_shid, curr_shid_c); - } - pairadd(&sohvp, vp); - break; - - default: - DEBUG("SoH Unknown TLV %i len=%i\n", tlv.tlv_type, tlv.tlv_len); - break; - } - - data += tlv.tlv_len; - data_len -= tlv.tlv_len; - - } - - return 0; -} diff --git a/src/main/soh.c b/src/main/soh.c new file mode 100644 index 0000000..3310c36 --- /dev/null +++ b/src/main/soh.c @@ -0,0 +1,579 @@ +/* + * soh.c contains the interfaces that are called from eap + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2010 Phil Mayers + */ + +#include +RCSID("$Id$") + +#include + +/* + * This code implements parsing of MS-SOH data into FreeRadius AVPs + * allowing for FreeRadius MS-NAP policies + */ + +typedef struct { + uint16_t tlv_type; /* ==7 */ + uint16_t tlv_len; + uint32_t tlv_vendor; + + /* then it's either an soh request or response */ + uint16_t soh_type; /* ==2 for request, 1 for response */ + uint16_t soh_len; + + /* an soh-response may now follow... */ +} eap_soh; + +typedef struct { + uint16_t outer_type; + uint16_t outer_len; + uint32_t vendor; + uint16_t inner_type; + uint16_t inner_len; +} soh_response; + +typedef struct { + uint16_t outer_type; + uint16_t outer_len; + uint32_t vendor; + uint8_t corrid[24]; + uint8_t intent; + uint8_t content_type; +} soh_mode_subheader; + +typedef struct { + uint16_t tlv_type; + uint16_t tlv_len; +} soh_tlv; + +/* utils for pulling big-endian 2/3/4 byte integers + * caller must ensure enough data exists at "p" + */ +uint16_t soh_pull_be_16(const uint8_t *p) { + uint16_t r; + + r = *p++ << 8; + r += *p++; + + return r; +} +uint32_t soh_pull_be_24(const uint8_t *p) { + uint32_t r; + + r = *p++ << 16; + r += *p++ << 8; + r += *p++; + + return r; +} +uint32_t soh_pull_be_32(const uint8_t *p) { + uint32_t r; + + r = *p++ << 24; + r += *p++ << 16; + r += *p++ << 8; + r += *p++; + + return r; +} + +/* + * This parses the microsoft type/value (note: NOT type/length/value) data; see + * section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip + * unknown types; we need to know their length ahead of time. Therefore, we abort + * if we find an unknown type. + */ +static int eapsoh_mstlv(VALUE_PAIR *sohvp, const uint8_t *p, unsigned int data_len) { + VALUE_PAIR *vp; + uint8_t c; + int t; + + while (data_len > 0) { + c = *p++; + data_len--; + + switch (c) { + case 1: + /* MS-Machine-Inventory-Packet + * MS-SOH section 2.2.4.1 + */ + if (data_len < 18) { + DEBUG("insufficient data for MS-Machine-Inventory-Packet\n"); + return 0; + } + data_len -= 18; + + vp = pairmake("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ); + pairadd(&sohvp, vp); + + vp = pairmake("SoH-MS-Machine-OS-version", NULL, T_OP_EQ); + vp->vp_integer = soh_pull_be_32(p); p+=4; + pairadd(&sohvp, vp); + + vp = pairmake("SoH-MS-Machine-OS-release", NULL, T_OP_EQ); + vp->vp_integer = soh_pull_be_32(p); p+=4; + pairadd(&sohvp, vp); + + vp = pairmake("SoH-MS-Machine-OS-build", NULL, T_OP_EQ); + vp->vp_integer = soh_pull_be_32(p); p+=4; + pairadd(&sohvp, vp); + + vp = pairmake("SoH-MS-Machine-SP-version", NULL, T_OP_EQ); + vp->vp_integer = soh_pull_be_16(p); p+=2; + pairadd(&sohvp, vp); + + vp = pairmake("SoH-MS-Machine-SP-release", NULL, T_OP_EQ); + vp->vp_integer = soh_pull_be_16(p); p+=2; + pairadd(&sohvp, vp); + + vp = pairmake("SoH-MS-Machine-Processor", NULL, T_OP_EQ); + vp->vp_integer = soh_pull_be_16(p); p+=2; + pairadd(&sohvp, vp); + + break; + + case 2: + /* MS-Quarantine-State - FIXME: currently unhandled + * MS-SOH 2.2.4.1 + * + * 1 byte reserved + * 1 byte flags + * 8 bytes NT Time field (100-nanosec since 1 Jan 1601) + * 2 byte urilen + * N bytes uri + */ + p += 10; + t = soh_pull_be_16(p); /* t == uri len */ + p += 2; + p += t; + data_len -= 12 + t; + break; + + case 3: + /* MS-Packet-Info + * MS-SOH 2.2.4.3 + */ + DEBUG("SoH MS-Packet-Info %s vers=%i\n", *p & 0x10 ? "request" : "response", *p & 0xf); + p++; + data_len--; + break; + + case 4: + /* MS-SystemGenerated-Ids - FIXME: currently unhandled + * MS-SOH 2.2.4.4 + * + * 2 byte length + * N bytes (3 bytes IANA enterprise# + 1 byte component id#) + */ + t = soh_pull_be_16(p); + p += 2; + p += t; + data_len -= 2 + t; + break; + + case 5: + /* MS-MachineName + * MS-SOH 2.2.4.5 + * + * 1 byte namelen + * N bytes name + */ + t = soh_pull_be_16(p); + p += 2; + + vp = pairmake("SoH-MS-Machine-Name", NULL, T_OP_EQ); + memcpy(vp->vp_strvalue, p, t); + vp->vp_strvalue[t] = 0; + + pairadd(&sohvp, vp); + p += t; + data_len -= 2 + t; + break; + + case 6: + /* MS-CorrelationId + * MS-SOH 2.2.4.6 + * + * 24 bytes opaque binary which we might, in future, have + * to echo back to the client in a final SoHR + */ + vp = pairmake("SoH-MS-Correlation-Id", NULL, T_OP_EQ); + memcpy(vp->vp_octets, p, 24); + vp->length = 24; + pairadd(&sohvp, vp); + p += 24; + data_len -= 24; + break; + + case 7: + /* MS-Installed-Shvs - FIXME: currently unhandled + * MS-SOH 2.2.4.7 + * + * 2 bytes length + * N bytes (3 bytes IANA enterprise# + 1 byte component id#) + */ + t = soh_pull_be_16(p); + p += 2; + p += t; + data_len -= 2 + t; + break; + + case 8: + /* MS-Machine-Inventory-Ex + * MS-SOH 2.2.4.8 + * + * 4 bytes reserved + * 1 byte product type (client=1 domain_controller=2 server=3) + */ + p += 4; + vp = pairmake("SoH-MS-Machine-Role", NULL, T_OP_EQ); + vp->vp_integer = *p; + pairadd(&sohvp, vp); + p++; + data_len -= 5; + break; + + default: + DEBUG("SoH Unknown MS TV %i stopping\n", c); + return 0; + } + } + return 1; +} + +static const char* clientstatus2str(uint32_t hcstatus) { + switch (hcstatus) { + /* this lot should all just be for windows updates */ + case 0xff0005: + return "wua-ok"; + case 0xff0006: + return "wua-missing"; + case 0xff0008: + return "wua-not-started"; + case 0xc0ff000c: + return "wua-no-wsus-server"; + case 0xc0ff000d: + return "wua-no-wsus-clientid"; + case 0xc0ff000e: + return "wua-disabled"; + case 0xc0ff000f: + return "wua-comm-failure"; + + /* these next 3 are for all health-classes */ + case 0xc0ff0002: + return "not-installed"; + case 0xc0ff0003: + return "down"; + case 0xc0ff0018: + return "not-started"; + } + return NULL; +} + +static const char* healthclass2str(uint8_t hc) { + switch (hc) { + case 0: + return "firewall"; + case 1: + return "antivirus"; + case 2: + return "antispyware"; + case 3: + return "updates"; + case 4: + return "security-updates"; + } + return NULL; +} + +int soh_verify(VALUE_PAIR *sohvp, const uint8_t *data, unsigned int data_len) { + + VALUE_PAIR *vp; + eap_soh hdr; + soh_response resp; + soh_mode_subheader mode; + soh_tlv tlv; + int curr_shid=-1, curr_shid_c=-1, curr_hc=-1; + + hdr.tlv_type = soh_pull_be_16(data); data += 2; + hdr.tlv_len = soh_pull_be_16(data); data += 2; + hdr.tlv_vendor = soh_pull_be_32(data); data += 4; + + if (hdr.tlv_type != 7 || hdr.tlv_vendor != 0x137) { + DEBUG("SoH payload is %i %08x not a ms-vendor packet\n", hdr.tlv_type, hdr.tlv_vendor); + return -1; + } + + hdr.soh_type = soh_pull_be_16(data); data += 2; + hdr.soh_len = soh_pull_be_16(data); data += 2; + if (hdr.soh_type != 1) { + DEBUG("SoH tlv %04x is not a response\n", hdr.soh_type); + return -1; + } + + /* FIXME: check for sufficient data */ + resp.outer_type = soh_pull_be_16(data); data += 2; + resp.outer_len = soh_pull_be_16(data); data += 2; + resp.vendor = soh_pull_be_32(data); data += 4; + resp.inner_type = soh_pull_be_16(data); data += 2; + resp.inner_len = soh_pull_be_16(data); data += 2; + + + if (resp.outer_type!=7 || resp.vendor != 0x137) { + DEBUG("SoH response outer type %i/vendor %08x not recognised\n", resp.outer_type, resp.vendor); + return -1; + } + switch (resp.inner_type) { + case 1: + /* no mode sub-header */ + DEBUG("SoH without mode subheader\n"); + break; + case 2: + mode.outer_type = soh_pull_be_16(data); data += 2; + mode.outer_len = soh_pull_be_16(data); data += 2; + mode.vendor = soh_pull_be_32(data); data += 4; + memcpy(mode.corrid, data, 24); data += 24; + mode.intent = data[0]; + mode.content_type = data[1]; + data += 2; + + if (mode.outer_type != 7 || mode.vendor != 0x137 || mode.content_type != 0) { + DEBUG("SoH mode subheader outer type %i/vendor %08x/content type %i invalid\n", mode.outer_type, mode.vendor, mode.content_type); + return -1; + } + DEBUG("SoH with mode subheader\n"); + break; + default: + DEBUG("SoH invalid inner type %i\n", resp.inner_type); + return -1; + } + + /* subtract off the relevant amount of data */ + if (resp.inner_type==2) { + data_len = resp.inner_len - 34; + } else { + data_len = resp.inner_len; + } + + /* TLV + * MS-SOH 2.2.1 + * See also 2.2.3 + * + * 1 bit mandatory + * 1 bit reserved + * 14 bits tlv type + * 2 bytes tlv length + * N bytes payload + * + */ + while (data_len >= 4) { + tlv.tlv_type = soh_pull_be_16(data); data += 2; + tlv.tlv_len = soh_pull_be_16(data); data += 2; + + data_len -= 4; + + switch (tlv.tlv_type) { + case 2: + /* System-Health-Id TLV + * MS-SOH 2.2.3.1 + * + * 3 bytes IANA/SMI vendor code + * 1 byte component (i.e. within vendor, which SoH component + */ + curr_shid = soh_pull_be_24(data); + curr_shid_c = data[3]; + DEBUG("SoH System-Health-ID vendor %08x component=%i\n", curr_shid, curr_shid_c); + break; + + case 7: + /* Vendor-Specific packet + * MS-SOH 2.2.3.3 + * + * 4 bytes vendor, supposedly ignored by NAP + * N bytes payload; for Microsoft component#0 this is the MS TV stuff + */ + if (curr_shid==0x137 && curr_shid_c==0) { + DEBUG("SoH MS type-value payload\n"); + eapsoh_mstlv(sohvp, data + 4, tlv.tlv_len - 4); + } else { + DEBUG("SoH unhandled vendor-specific TLV %08x/component=%i %i bytes payload\n", curr_shid, curr_shid_c, tlv.tlv_len); + } + break; + + case 8: + /* Health-Class + * MS-SOH 2.2.3.5.6 + * + * 1 byte integer + */ + DEBUG("SoH Health-Class %i\n", data[0]); + curr_hc = data[0]; + break; + + case 9: + /* Software-Version + * MS-SOH 2.2.3.5.7 + * + * 1 byte integer + */ + DEBUG("SoH Software-Version %i\n", data[0]); + break; + + case 11: + /* Health-Class status + * MS-SOH 2.2.3.5.9 + * + * variable data; for the MS System Health vendor, these are 4-byte + * integers which are a really, really dumb format: + * + * 28 bits ignore + * 1 bit - 1==product snoozed + * 1 bit - 1==microsoft product + * 1 bit - 1==product up-to-date + * 1 bit - 1==product enabled + */ + DEBUG("SoH Health-Class-Status - current shid=%08x component=%i\n", curr_shid, curr_shid_c); + + if (curr_shid==0x137 && curr_shid_c==128) { + + const char *s, *t; + uint32_t hcstatus = soh_pull_be_32(data); + + DEBUG("SoH Health-Class-Status microsoft DWORD=%08x\n", hcstatus); + + vp = pairmake("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ); + switch (curr_hc) { + case 4: + /* security updates */ + s = "security-updates"; + switch (hcstatus) { + case 0xff0005: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok all-installed", s); + break; + case 0xff0006: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn some-missing", s); + break; + case 0xff0008: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn never-started", s); + break; + case 0xc0ff000c: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error no-wsus-srv", s); + break; + case 0xc0ff000d: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error no-wsus-clid", s); + break; + case 0xc0ff000e: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn wsus-disabled", s); + break; + case 0xc0ff000f: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error comm-failure", s); + break; + case 0xc0ff0010: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn needs-reboot", s); + break; + default: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus); + break; + } + break; + + case 3: + /* auto updates */ + s = "auto-updates"; + switch (hcstatus) { + case 1: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn disabled", s); + break; + case 2: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=check-only", s); + break; + case 3: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=download", s); + break; + case 4: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=install", s); + break; + case 5: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn unconfigured", s); + break; + case 0xc0ff0003: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn service-down", s); + break; + case 0xc0ff0018: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn never-started", s); + break; + default: + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus); + break; + } + break; + + default: + /* other - firewall, antivirus, antispyware */ + s = healthclass2str(curr_hc); + if (s) { + /* bah. this is vile. stupid microsoft + */ + if (hcstatus & 0xff000000) { + /* top octet non-zero means an error + * FIXME: is this always correct? MS-WSH 2.2.8 is unclear + */ + t = clientstatus2str(hcstatus); + if (t) { + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %s", s, t); + } else { + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus); + } + } else { + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), + "%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i", + s, + hcstatus & 0x8 ? 1 : 0, + hcstatus & 0x4 ? 1 : 0, + hcstatus & 0x2 ? 1 : 0, + hcstatus & 0x1 ? 1 : 0 + ); + } + } else { + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%i unknown %08x", curr_hc, hcstatus); + } + break; + } + } else { + vp = pairmake("SoH-Other-Health-Status", NULL, T_OP_EQ); + /* FIXME: what to do with the payload? */ + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%08x/%i ?", curr_shid, curr_shid_c); + } + pairadd(&sohvp, vp); + break; + + default: + DEBUG("SoH Unknown TLV %i len=%i\n", tlv.tlv_type, tlv.tlv_len); + break; + } + + data += tlv.tlv_len; + data_len -= tlv.tlv_len; + + } + + return 0; +} -- 1.6.6.1