>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