/* * This program is 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 */ /** * $Id$ * @file rlm_soh.c * @brief Decodes Microsoft's Statement of Health sub-protocol. * * @copyright 2010 Phil Mayers */ RCSID("$Id$") #include #include #include #include typedef struct rlm_soh_t { char const *xlat_name; bool dhcp; } rlm_soh_t; /* * Not sure how to make this useful yet... */ static ssize_t soh_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { VALUE_PAIR* vp[6]; char const *osname; /* * There will be no point unless SoH-Supported = yes */ vp[0] = fr_pair_find_by_num(request->packet->vps, PW_SOH_SUPPORTED, 0, TAG_ANY); if (!vp[0]) return 0; if (strncasecmp(fmt, "OS", 2) == 0) { /* OS vendor */ vp[0] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_VENDOR, 0, TAG_ANY); vp[1] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_VERSION, 0, TAG_ANY); vp[2] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_RELEASE, 0, TAG_ANY); vp[3] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_BUILD, 0, TAG_ANY); vp[4] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_SP_VERSION, 0, TAG_ANY); vp[5] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_SP_RELEASE, 0, TAG_ANY); if (vp[0] && vp[0]->vp_integer == VENDORPEC_MICROSOFT) { if (!vp[1]) { snprintf(out, outlen, "Windows unknown"); } else { switch (vp[1]->vp_integer) { case 7: osname = "7"; break; case 6: osname = "Vista"; break; case 5: osname = "XP"; break; default: osname = "Other"; break; } snprintf(out, outlen, "Windows %s %d.%d.%d sp %d.%d", osname, vp[1]->vp_integer, vp[2] ? vp[2]->vp_integer : 0, vp[3] ? vp[3]->vp_integer : 0, vp[4] ? vp[4]->vp_integer : 0, vp[5] ? vp[5]->vp_integer : 0 ); } return strlen(out); } } return 0; } static const CONF_PARSER module_config[] = { /* * Do SoH over DHCP? */ { "dhcp", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_soh_t, dhcp), "no" }, CONF_PARSER_TERMINATOR }; static int mod_bootstrap(CONF_SECTION *conf, void *instance) { char const *name; rlm_soh_t *inst = instance; name = cf_section_name2(conf); if (!name) name = cf_section_name1(conf); inst->xlat_name = name; if (!inst->xlat_name) return -1; xlat_register(inst->xlat_name, soh_xlat, NULL, inst); return 0; } #ifdef WITH_DHCP static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request) { int rcode; VALUE_PAIR *vp; rlm_soh_t *inst = instance; if (!inst->dhcp) return RLM_MODULE_NOOP; vp = fr_pair_find_by_num(request->packet->vps, 43, DHCP_MAGIC_VENDOR, TAG_ANY); if (vp) { /* * vendor-specific options contain * * vendor opt 220/0xdc - SoH payload, or null byte to probe, or string * "NAP" to indicate server-side support for SoH in OFFERs * * vendor opt 222/0xde - SoH correlation ID as utf-16 string, yuck... */ uint8_t vopt, vlen; uint8_t const *data; data = vp->vp_octets; while (data < vp->vp_octets + vp->vp_length) { vopt = *data++; vlen = *data++; switch (vopt) { case 220: if (vlen <= 1) { uint8_t *p; RDEBUG("SoH adding NAP marker to DHCP reply"); /* client probe; send "NAP" in the reply */ vp = fr_pair_afrom_num(request->reply, 43, DHCP_MAGIC_VENDOR); vp->vp_length = 5; vp->vp_octets = p = talloc_array(vp, uint8_t, vp->vp_length); p[0] = 220; p[1] = 3; p[4] = 'N'; p[3] = 'A'; p[2] = 'P'; fr_pair_add(&request->reply->vps, vp); } else { RDEBUG("SoH decoding NAP from DHCP request"); /* SoH payload */ rcode = soh_verify(request, data, vlen); if (rcode < 0) { return RLM_MODULE_FAIL; } } break; default: /* nothing to do */ break; } data += vlen; } return RLM_MODULE_OK; } return RLM_MODULE_NOOP; } #endif static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void * instance, REQUEST *request) { VALUE_PAIR *vp; int rv; /* try to find the MS-SoH payload */ vp = fr_pair_find_by_num(request->packet->vps, 55, VENDORPEC_MICROSOFT, TAG_ANY); if (!vp) { RDEBUG("SoH radius VP not found"); return RLM_MODULE_NOOP; } RDEBUG("SoH radius VP found"); /* decode it */ rv = soh_verify(request, vp->vp_octets, vp->vp_length); if (rv < 0) { return RLM_MODULE_FAIL; } return RLM_MODULE_OK; } extern module_t rlm_soh; module_t rlm_soh = { .magic = RLM_MODULE_INIT, .name = "soh", .type = RLM_TYPE_THREAD_SAFE, .inst_size = sizeof(rlm_soh_t), .config = module_config, .bootstrap = mod_bootstrap, .methods = { [MOD_AUTHORIZE] = mod_authorize, #ifdef WITH_DHCP [MOD_POST_AUTH] = mod_post_auth #endif }, };