2 * soh.c contains the interfaces that are called from eap
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2010 Phil Mayers <p.mayers@imperial.ac.uk>
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/soh.h>
29 * This code implements parsing of MS-SOH data into FreeRadius AVPs
30 * allowing for FreeRadius MS-NAP policies
34 uint16_t tlv_type; /* ==7 */
38 /* then it's either an soh request or response */
39 uint16_t soh_type; /* ==2 for request, 1 for response */
42 /* an soh-response may now follow... */
67 /* utils for pulling big-endian 2/3/4 byte integers
68 * caller must ensure enough data exists at "p"
70 uint16_t soh_pull_be_16(const uint8_t *p) {
78 uint32_t soh_pull_be_24(const uint8_t *p) {
87 uint32_t soh_pull_be_32(const uint8_t *p) {
99 * This parses the microsoft type/value (note: NOT type/length/value) data; see
100 * section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip
101 * unknown types; we need to know their length ahead of time. Therefore, we abort
102 * if we find an unknown type.
104 static int eapsoh_mstlv(REQUEST *request, VALUE_PAIR *sohvp, const uint8_t *p, unsigned int data_len) {
109 while (data_len > 0) {
115 /* MS-Machine-Inventory-Packet
116 * MS-SOH section 2.2.4.1
119 RDEBUG("insufficient data for MS-Machine-Inventory-Packet");
124 vp = pairmake("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
127 vp = pairmake("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
128 vp->vp_integer = soh_pull_be_32(p); p+=4;
131 vp = pairmake("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
132 vp->vp_integer = soh_pull_be_32(p); p+=4;
135 vp = pairmake("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
136 vp->vp_integer = soh_pull_be_32(p); p+=4;
139 vp = pairmake("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
140 vp->vp_integer = soh_pull_be_16(p); p+=2;
143 vp = pairmake("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
144 vp->vp_integer = soh_pull_be_16(p); p+=2;
147 vp = pairmake("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
148 vp->vp_integer = soh_pull_be_16(p); p+=2;
154 /* MS-Quarantine-State - FIXME: currently unhandled
159 * 8 bytes NT Time field (100-nanosec since 1 Jan 1601)
164 t = soh_pull_be_16(p); /* t == uri len */
174 RDEBUG("SoH MS-Packet-Info %s vers=%i", *p & 0x10 ? "request" : "response", *p & 0xf);
180 /* MS-SystemGenerated-Ids - FIXME: currently unhandled
184 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
186 t = soh_pull_be_16(p);
199 t = soh_pull_be_16(p);
202 vp = pairmake("SoH-MS-Machine-Name", NULL, T_OP_EQ);
203 memcpy(vp->vp_strvalue, p, t);
204 vp->vp_strvalue[t] = 0;
215 * 24 bytes opaque binary which we might, in future, have
216 * to echo back to the client in a final SoHR
218 vp = pairmake("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
219 memcpy(vp->vp_octets, p, 24);
227 /* MS-Installed-Shvs - FIXME: currently unhandled
231 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
233 t = soh_pull_be_16(p);
240 /* MS-Machine-Inventory-Ex
244 * 1 byte product type (client=1 domain_controller=2 server=3)
247 vp = pairmake("SoH-MS-Machine-Role", NULL, T_OP_EQ);
255 RDEBUG("SoH Unknown MS TV %i stopping", c);
262 static const char* clientstatus2str(uint32_t hcstatus) {
264 /* this lot should all just be for windows updates */
268 return "wua-missing";
270 return "wua-not-started";
272 return "wua-no-wsus-server";
274 return "wua-no-wsus-clientid";
276 return "wua-disabled";
278 return "wua-comm-failure";
280 /* these next 3 are for all health-classes */
282 return "not-installed";
286 return "not-started";
291 static const char* healthclass2str(uint8_t hc) {
298 return "antispyware";
302 return "security-updates";
307 int soh_verify(REQUEST *request, VALUE_PAIR *sohvp, const uint8_t *data, unsigned int data_len) {
312 soh_mode_subheader mode;
314 int i, curr_shid=-1, curr_shid_c=-1, curr_hc=-1;
316 hdr.tlv_type = soh_pull_be_16(data); data += 2;
317 hdr.tlv_len = soh_pull_be_16(data); data += 2;
318 hdr.tlv_vendor = soh_pull_be_32(data); data += 4;
320 if (hdr.tlv_type != 7 || hdr.tlv_vendor != 0x137) {
321 RDEBUG("SoH payload is %i %08x not a ms-vendor packet", hdr.tlv_type, hdr.tlv_vendor);
325 hdr.soh_type = soh_pull_be_16(data); data += 2;
326 hdr.soh_len = soh_pull_be_16(data); data += 2;
327 if (hdr.soh_type != 1) {
328 RDEBUG("SoH tlv %04x is not a response", hdr.soh_type);
332 /* FIXME: check for sufficient data */
333 resp.outer_type = soh_pull_be_16(data); data += 2;
334 resp.outer_len = soh_pull_be_16(data); data += 2;
335 resp.vendor = soh_pull_be_32(data); data += 4;
336 resp.inner_type = soh_pull_be_16(data); data += 2;
337 resp.inner_len = soh_pull_be_16(data); data += 2;
340 if (resp.outer_type!=7 || resp.vendor != 0x137) {
341 RDEBUG("SoH response outer type %i/vendor %08x not recognised", resp.outer_type, resp.vendor);
344 switch (resp.inner_type) {
346 /* no mode sub-header */
347 RDEBUG("SoH without mode subheader");
350 mode.outer_type = soh_pull_be_16(data); data += 2;
351 mode.outer_len = soh_pull_be_16(data); data += 2;
352 mode.vendor = soh_pull_be_32(data); data += 4;
353 memcpy(mode.corrid, data, 24); data += 24;
354 mode.intent = data[0];
355 mode.content_type = data[1];
358 if (mode.outer_type != 7 || mode.vendor != 0x137 || mode.content_type != 0) {
359 RDEBUG("SoH mode subheader outer type %i/vendor %08x/content type %i invalid", mode.outer_type, mode.vendor, mode.content_type);
362 RDEBUG("SoH with mode subheader");
365 RDEBUG("SoH invalid inner type %i", resp.inner_type);
369 /* subtract off the relevant amount of data */
370 if (resp.inner_type==2) {
371 data_len = resp.inner_len - 34;
373 data_len = resp.inner_len;
387 while (data_len >= 4) {
388 tlv.tlv_type = soh_pull_be_16(data); data += 2;
389 tlv.tlv_len = soh_pull_be_16(data); data += 2;
393 switch (tlv.tlv_type) {
395 /* System-Health-Id TLV
398 * 3 bytes IANA/SMI vendor code
399 * 1 byte component (i.e. within vendor, which SoH component
401 curr_shid = soh_pull_be_24(data);
402 curr_shid_c = data[3];
403 RDEBUG2("SoH System-Health-ID vendor %08x component=%i", curr_shid, curr_shid_c);
407 /* Vendor-Specific packet
410 * 4 bytes vendor, supposedly ignored by NAP
411 * N bytes payload; for Microsoft component#0 this is the MS TV stuff
413 if (curr_shid==0x137 && curr_shid_c==0) {
414 RDEBUG2("SoH MS type-value payload");
415 eapsoh_mstlv(request, sohvp, data + 4, tlv.tlv_len - 4);
417 RDEBUG2("SoH unhandled vendor-specific TLV %08x/component=%i %i bytes payload", curr_shid, curr_shid_c, tlv.tlv_len);
427 RDEBUG2("SoH Health-Class %i", data[0]);
437 RDEBUG2("SoH Software-Version %i", data[0]);
441 /* Health-Class status
444 * variable data; for the MS System Health vendor, these are 4-byte
445 * integers which are a really, really dumb format:
448 * 1 bit - 1==product snoozed
449 * 1 bit - 1==microsoft product
450 * 1 bit - 1==product up-to-date
451 * 1 bit - 1==product enabled
453 RDEBUG2("SoH Health-Class-Status - current shid=%08x component=%i", curr_shid, curr_shid_c);
455 if (curr_shid==0x137 && curr_shid_c==128) {
458 uint32_t hcstatus = soh_pull_be_32(data);
460 RDEBUG2("SoH Health-Class-Status microsoft DWORD=%08x", hcstatus);
462 vp = pairmake("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ);
465 /* security updates */
466 s = "security-updates";
469 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok all-installed", s);
472 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn some-missing", s);
475 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn never-started", s);
478 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error no-wsus-srv", s);
481 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error no-wsus-clid", s);
484 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn wsus-disabled", s);
487 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error comm-failure", s);
490 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn needs-reboot", s);
493 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus);
503 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn disabled", s);
506 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=check-only", s);
509 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=download", s);
512 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s ok action=install", s);
515 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn unconfigured", s);
518 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn service-down", s);
521 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s warn never-started", s);
524 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus);
530 /* other - firewall, antivirus, antispyware */
531 s = healthclass2str(curr_hc);
533 /* bah. this is vile. stupid microsoft
535 if (hcstatus & 0xff000000) {
536 /* top octet non-zero means an error
537 * FIXME: is this always correct? MS-WSH 2.2.8 is unclear
539 t = clientstatus2str(hcstatus);
541 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %s", s, t);
543 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s error %08x", s, hcstatus);
546 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue),
547 "%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i",
549 hcstatus & 0x8 ? 1 : 0,
550 hcstatus & 0x4 ? 1 : 0,
551 hcstatus & 0x2 ? 1 : 0,
552 hcstatus & 0x1 ? 1 : 0
556 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%i unknown %08x", curr_hc, hcstatus);
561 vp = pairmake("SoH-Other-Health-Status", NULL, T_OP_EQ);
562 /* FIXME: what to do with the payload? */
563 snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%08x/%i ?", curr_shid, curr_shid_c);
569 RDEBUG2("SoH Unknown TLV %i len=%i", tlv.tlv_type, tlv.tlv_len);
574 data_len -= tlv.tlv_len;