/** Add a module failure message VALUE_PAIR to the request */ void module_failure_msg(REQUEST *request, char const *fmt, ...) { va_list ap; char *p; VALUE_PAIR *vp; if (!fmt || !request->packet) { va_start(ap, fmt); va_end(ap); return; } va_start(ap, fmt); vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0); if (!vp) { va_end(ap); return; } p = talloc_vasprintf(vp, fmt, ap); if (request->module && *request->module) { pairsprintf(vp, "%s: %s", request->module, p); } else { pairsprintf(vp, "%s", p); } talloc_free(p); pairadd(&request->packet->vps, vp); }
/** Add a module failure message VALUE_PAIR to the request */ void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap) { char *p; VALUE_PAIR *vp; va_list aq; if (!fmt || !request->packet) { return; } /* * If we don't copy the original ap we get a segfault from vasprintf. This is apparently * due to ap sometimes being implemented with a stack offset which is invalidated if * ap is passed into another function. See here: * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html * * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when * running unit tests which generate errors under CI. */ va_copy(aq, ap); p = talloc_vasprintf(request, fmt, aq); va_end(aq); MEM(vp = pairmake_packet("Module-Failure-Message", NULL, T_OP_ADD)); if (request->module && (request->module[0] != '\0')) { pairsprintf(vp, "%s: %s", request->module, p); } else { pairsprintf(vp, "%s", p); } talloc_free(p); }
/** Add a module failure message VALUE_PAIR to the request */ void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap) { char *p; VALUE_PAIR *vp; va_list aq; if (!fmt || !request->packet) { return; } vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0); if (!vp) { return; } /* * If we don't copy the original ap we get a segfault from vasprintf. This is apparently * due to ap sometimes being implemented with a stack offset which is invalidated if * ap is passed into another function. See here: * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html * * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when * running unit tests which generate errors under CI. */ va_copy(aq, ap); p = talloc_vasprintf(vp, fmt, aq); talloc_set_type(p, char); va_end(aq); if (request->module && *request->module) { pairsprintf(vp, "%s: %s", request->module, p); } else { pairsprintf(vp, "%s", p); } talloc_free(p); pairadd(&request->packet->vps, vp); }
/** * @brief Parse the MS-SOH response in data and update sohvp. * * Note that sohvp might still have been updated in event of a failure. * * @param request Current request * @param data MS-SOH blob * @param data_len length of MS-SOH blob * * @return 0 on success, -1 on failure * */ int soh_verify(REQUEST *request, uint8_t const *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; rad_assert(request->packet != NULL); 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) { RDEBUG("SoH payload is %i %08x not a ms-vendor packet", 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) { RDEBUG("SoH tlv %04x is not a response", 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) { RDEBUG("SoH response outer type %i/vendor %08x not recognised", resp.outer_type, resp.vendor); return -1; } switch (resp.inner_type) { case 1: /* no mode sub-header */ RDEBUG("SoH without mode subheader"); 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) { RDEBUG3("SoH mode subheader outer type %i/vendor %08x/content type %i invalid", mode.outer_type, mode.vendor, mode.content_type); return -1; } RDEBUG3("SoH with mode subheader"); break; default: RDEBUG("SoH invalid inner type %i", 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]; RDEBUG2("SoH System-Health-ID vendor %08x component=%i", 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) { RDEBUG2("SoH MS type-value payload"); eapsoh_mstlv(request, data + 4, tlv.tlv_len - 4); } else { RDEBUG2("SoH unhandled vendor-specific TLV %08x/component=%i %i bytes payload", curr_shid, curr_shid_c, tlv.tlv_len); } break; case 8: /* Health-Class * MS-SOH 2.2.3.5.6 * * 1 byte integer */ RDEBUG2("SoH Health-Class %i", data[0]); curr_hc = data[0]; break; case 9: /* Software-Version * MS-SOH 2.2.3.5.7 * * 1 byte integer */ RDEBUG2("SoH Software-Version %i", 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 */ RDEBUG2("SoH Health-Class-Status - current shid=%08x component=%i", curr_shid, curr_shid_c); if (curr_shid == 0x137 && curr_shid_c == 128) { char const *s, *t; uint32_t hcstatus = soh_pull_be_32(data); RDEBUG2("SoH Health-Class-Status microsoft DWORD=%08x", hcstatus); vp = pairmake_packet("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ); if (!vp) return 0; switch (curr_hc) { case 4: /* security updates */ s = "security-updates"; switch (hcstatus) { case 0xff0005: pairsprintf(vp, "%s ok all-installed", s); break; case 0xff0006: pairsprintf(vp, "%s warn some-missing", s); break; case 0xff0008: pairsprintf(vp, "%s warn never-started", s); break; case 0xc0ff000c: pairsprintf(vp, "%s error no-wsus-srv", s); break; case 0xc0ff000d: pairsprintf(vp, "%s error no-wsus-clid", s); break; case 0xc0ff000e: pairsprintf(vp, "%s warn wsus-disabled", s); break; case 0xc0ff000f: pairsprintf(vp, "%s error comm-failure", s); break; case 0xc0ff0010: pairsprintf(vp, "%s warn needs-reboot", s); break; default: pairsprintf(vp, "%s error %08x", s, hcstatus); break; } break; case 3: /* auto updates */ s = "auto-updates"; switch (hcstatus) { case 1: pairsprintf(vp, "%s warn disabled", s); break; case 2: pairsprintf(vp, "%s ok action=check-only", s); break; case 3: pairsprintf(vp, "%s ok action=download", s); break; case 4: pairsprintf(vp, "%s ok action=install", s); break; case 5: pairsprintf(vp, "%s warn unconfigured", s); break; case 0xc0ff0003: pairsprintf(vp, "%s warn service-down", s); break; case 0xc0ff0018: pairsprintf(vp, "%s warn never-started", s); break; default: pairsprintf(vp, "%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) { pairsprintf(vp, "%s error %s", s, t); } else { pairsprintf(vp, "%s error %08x", s, hcstatus); } } else { pairsprintf(vp, "%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 { pairsprintf(vp, "%i unknown %08x", curr_hc, hcstatus); } break; } } else { vp = pairmake_packet("SoH-MS-Health-Other", NULL, T_OP_EQ); if (!vp) return 0; /* FIXME: what to do with the payload? */ pairsprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c); } break; default: RDEBUG("SoH Unknown TLV %i len=%i", tlv.tlv_type, tlv.tlv_len); break; } data += tlv.tlv_len; data_len -= tlv.tlv_len; } return 0; }