/* * Check password. * * Returns: 0 OK * -1 Password fail * -2 Rejected (Auth-Type = Reject, send Port-Message back) * 1 End check & return, don't reply * * NOTE: NOT the same as the RLM_ values ! */ static int rad_check_password(REQUEST *request) { VALUE_PAIR *auth_type_pair; VALUE_PAIR *cur_config_item; int auth_type = -1; int result; int auth_type_count = 0; result = 0; /* * Look for matching check items. We skip the whole lot * if the authentication type is PW_AUTHTYPE_ACCEPT or * PW_AUTHTYPE_REJECT. */ cur_config_item = request->config_items; while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE, 0, TAG_ANY))) != NULL) { auth_type = auth_type_pair->vp_integer; auth_type_count++; RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type)); cur_config_item = auth_type_pair->next; if (auth_type == PW_AUTHTYPE_REJECT) { RDEBUG2("Auth-Type = Reject, rejecting user"); return -2; } } /* * Warn if more than one Auth-Type was found, because only the last * one found will actually be used. */ if (( auth_type_count > 1) && (debug_flag)) { radlog_request(L_ERR, 0, request, "Warning: Found %d auth-types on request for user '%s'", auth_type_count, request->username->vp_strvalue); } /* * This means we have a proxy reply or an accept and it wasn't * rejected in the above loop. So that means it is accepted and we * do no further authentication. */ if ((auth_type == PW_AUTHTYPE_ACCEPT) #ifdef WITH_PROXY || (request->proxy) #endif ) { RDEBUG2("Auth-Type = Accept, accepting the user"); return 0; } /* * Check that Auth-Type has been set, and reject if not. * * Do quick checks to see if Cleartext-Password or Crypt-Password have * been set, and complain if so. */ if (auth_type < 0) { if (pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) { RDEBUG2W("Please update your configuration, and remove 'Auth-Type = Crypt'"); RDEBUG2W("Use the PAP module instead."); } else if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) { RDEBUG2W("Please update your configuration, and remove 'Auth-Type = Local'"); RDEBUG2W("Use the PAP or CHAP modules instead."); } /* * The admin hasn't told us how to * authenticate the user, so we reject them! * * This is fail-safe. */ RDEBUG2E("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject"); return -2; } /* * See if there is a module that handles * this Auth-Type, and turn the RLM_ return * status into the values as defined at * the top of this function. */ result = module_authenticate(auth_type, request); switch (result) { /* * An authentication module FAIL * return code, or any return code that * is not expected from authentication, * is the same as an explicit REJECT! */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_UPDATED: case RLM_MODULE_USERLOCK: default: result = -1; break; case RLM_MODULE_OK: result = 0; break; case RLM_MODULE_HANDLED: result = 1; break; } return result; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no * replications succeeded, else ok. */ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, **vps, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2E("Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2E("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2E("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (!packet) { packet = rad_alloc(NULL, 1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUGE("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } vps = radius_list(request, list); if (!vps) { RDEBUGW("List '%s' doesn't exist for " "this packet", fr_int2str(pair_lists, list, "?unknown?")); rcode = RLM_MODULE_INVALID; goto done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto done; } } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "¿unknown?"),realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUGE("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: cleanup(packet); return rcode; }
/** Decode an attribute name into a string * * This expands the various formats: * - %{Name} * - %{xlat:name} * - %{Name:-Other} * * Calls radius_xlat() to do most of the work. * * @param[in] from string to expand. * @param[in,out] to buffer for output. * @param[in] freespace remaining in output buffer. * @param[in] request Current server request. * @param[in] func Optional function to escape output; passed to radius_xlat(). * @param[in] funcarg pointer to pass to escape function. * @return 0 on success, -1 on failure. */ static int decode_attribute(const char **from, char **to, int freespace, REQUEST *request, RADIUS_ESCAPE_STRING func, void *funcarg) { int do_length = 0; const char *module_name, *xlat_str; char *p, *q, *l, *next = NULL; int retlen=0; const xlat_t *c; int varlen; char buffer[8192]; q = *to; *q = '\0'; /* * Copy the input string to an intermediate buffer where * we can mangle it. */ varlen = rad_copy_variable(buffer, *from); if (varlen < 0) { RDEBUG2E("Badly formatted variable: %s", *from); return -1; } *from += varlen; /* * Kill the %{} around the data we are looking for. */ p = buffer; p[varlen - 1] = '\0'; /* */ p += 2; if (*p == '#') { p++; do_length = 1; } /* * Handle %{%{foo}:-%{bar}}, which is useful, too. * * Did I mention that this parser is garbage? */ if ((p[0] == '%') && (p[1] == '{')) { int len1, len2; int expand2 = FALSE; /* * 'p' is after the start of 'buffer', so we can * safely do this. */ len1 = rad_copy_variable(buffer, p); if (len1 < 0) { RDEBUG2E("Badly formatted variable: %s", p); return -1; } /* * They did %{%{foo}}, which is stupid, but allowed. */ if (!p[len1]) { RDEBUG2("Improperly nested variable; %%{%s}", p); return -1; } /* * It SHOULD be %{%{foo}:-%{bar}}. If not, it's * an error. */ if ((p[len1] != ':') || (p[len1 + 1] != '-')) { RDEBUG2("No trailing :- after variable at %s", p); return -1; } /* * Parse the second bit. The second bit can be * either %{foo}, or a string "foo", or a string * 'foo', or just a bare word: foo */ p += len1 + 2; l = buffer + len1 + 1; if ((p[0] == '%') && (p[1] == '{')) { len2 = rad_copy_variable(l, p); if (len2 < 0) { RDEBUG2E("Invalid text after :- at %s", p); return -1; } p += len2; expand2 = TRUE; } else if ((p[0] == '"') || p[0] == '\'') { getstring((const char **) &p, l, strlen(l)); } else { l = p; } /* * Expand the first one. If we did, exit the * conditional. */ retlen = radius_xlat(q, freespace, buffer, request, func, funcarg); if (retlen) { q += retlen; goto done; } RDEBUG2("\t... expanding second conditional"); /* * Expand / copy the second string if required. */ if (expand2) { retlen = radius_xlat(q, freespace, l, request, func, funcarg); if (retlen) { q += retlen; } } else { strlcpy(q, l, freespace); q += strlen(q); } /* * Else the output is an empty string. */ goto done; } /* * See if we're supposed to expand a module name. */ module_name = NULL; for (l = p; *l != '\0'; l++) { /* * module:string */ if (*l == ':') { module_name = p; /* start of name */ *l = '\0'; p = l + 1; break; } /* * Module names can't have spaces. */ if ((*l == ' ') || (*l == '\t')) break; } /* * %{name} is a simple attribute reference, * or regex reference. */ if (!module_name) { if (isdigit(*p) && !p[1]) { /* regex 0..8 */ module_name = xlat_str = p; } else { xlat_str = p; } goto do_xlat; } /* * Maybe it's the old-style %{foo:-bar} */ if (*p == '-') { RDEBUG2W("Deprecated conditional expansion \":-\". See \"man unlang\" for details"); p++; xlat_str = module_name; next = p; goto do_xlat; } /* * FIXME: For backwards "WTF" compatibility, check for * {...}, (after the :), and copy that, too. */ /* module name, followed by (possibly) per-module string */ xlat_str = p; do_xlat: /* * Just "foo". Maybe it's a magic attr, which doesn't * really exist. * * If we can't find that, then assume it's a dictionary * attribute in the request. * * Else if it's module:foo, look for module, and pass it "foo". */ if (!module_name) { c = xlat_find(xlat_str); if (!c) c = xlat_find("request"); } else { c = xlat_find(module_name); } if (!c) { if (!module_name) { RDEBUG2W("Unknown Attribute \"%s\" in string expansion \"%%%s\"", xlat_str, *from); } else { RDEBUG2W("Unknown module \"%s\" in string expansion \"%%%s\"", module_name, *from); } return -1; } if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'", c->module, xlat_str); if (func) { /* xlat to a temporary buffer, then escape */ char tmpbuf[8192]; retlen = c->do_xlat(c->instance, request, xlat_str, tmpbuf, sizeof(tmpbuf)); if (retlen > 0) { retlen = func(request, q, freespace, tmpbuf, funcarg); if (retlen > 0) { RDEBUG2("\tescape: \'%s\' -> \'%s\'", tmpbuf, q); } else if (retlen < 0) { RDEBUG2("String escape failed"); } } } else { retlen = c->do_xlat(c->instance, request, xlat_str, q, freespace); } if (retlen > 0) { if (do_length) { snprintf(q, freespace, "%d", retlen); retlen = strlen(q); } } else if (next) { /* * Expand the second bit. */ RDEBUG2("\t... expanding second conditional"); retlen = radius_xlat(q, freespace, next, request, func, funcarg); } q += retlen; done: *to = q; return 0; }
/* * Authenticate a previously sent challenge. */ static int gtc_authenticate(void *type_data, EAP_HANDLER *handler) { VALUE_PAIR *vp; EAP_DS *eap_ds = handler->eap_ds; rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) type_data; REQUEST *request = handler->request; /* * Get the Cleartext-Password for this user. */ rad_assert(handler->request != NULL); rad_assert(handler->stage == AUTHENTICATE); /* * Sanity check the response. We need at least one byte * of data. */ if (eap_ds->response->length <= 4) { radlog(L_ERR, "rlm_eap_gtc: corrupted data"); eap_ds->request->code = PW_EAP_FAILURE; return 0; } #if 0 if ((debug_flag > 2) && fr_log_fp) { int i; for (i = 0; i < eap_ds->response->length - 4; i++) { if ((i & 0x0f) == 0) fprintf(fr_log_fp, "%d: ", i); fprintf(fr_log_fp, "%02x ", eap_ds->response->type.data[i]); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); } } #endif /* * Handle passwords here. */ if (inst->auth_type == PW_AUTHTYPE_LOCAL) { /* * For now, do clear-text password authentication. */ vp = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY); if (!vp) { RDEBUG2E("Cleartext-Password is required for authentication."); eap_ds->request->code = PW_EAP_FAILURE; return 0; } if (eap_ds->response->type.length != vp->length) { RDEBUG2E("Passwords are of different length. %u %u", (unsigned) eap_ds->response->type.length, (unsigned) vp->length); eap_ds->request->code = PW_EAP_FAILURE; return 0; } if (memcmp(eap_ds->response->type.data, vp->vp_strvalue, vp->length) != 0) { RDEBUG2E("Passwords are different"); eap_ds->request->code = PW_EAP_FAILURE; return 0; } /* * EAP packets can be ~64k long maximum, and * we don't like that. */ } else if (eap_ds->response->type.length <= 128) { int rcode; /* * If there was a User-Password in the request, * why the heck are they using EAP-GTC? */ pairdelete(&handler->request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); vp = pairmake("User-Password", "", T_OP_EQ); if (!vp) { radlog(L_ERR, "rlm_eap_gtc: out of memory"); return 0; } vp->length = eap_ds->response->type.length; memcpy(vp->vp_strvalue, eap_ds->response->type.data, vp->length); vp->vp_strvalue[vp->length] = 0; /* * Add the password to the request, and allow * another module to do the work of authenticating it. */ pairadd(&handler->request->packet->vps, vp); handler->request->password = vp; /* * This is a wild & crazy hack. */ rcode = module_authenticate(inst->auth_type, handler->request); if (rcode != RLM_MODULE_OK) { eap_ds->request->code = PW_EAP_FAILURE; return 0; } } else { radlog(L_ERR, "rlm_eap_gtc: Response is too large to understand"); eap_ds->request->code = PW_EAP_FAILURE; return 0; } eap_ds->request->code = PW_EAP_SUCCESS; return 1; }
/* * Verify that the diameter packet is valid. */ static int diameter_verify(REQUEST *request, const uint8_t *data, unsigned int data_len) { uint32_t attr; uint32_t length; unsigned int hdr_len; unsigned int remaining = data_len; while (remaining > 0) { hdr_len = 12; if (remaining < hdr_len) { RDEBUG2(" Diameter attribute is too small (%u) to contain a Diameter header", remaining); return 0; } memcpy(&attr, data, sizeof(attr)); attr = ntohl(attr); memcpy(&length, data + 4, sizeof(length)); length = ntohl(length); if ((data[4] & 0x80) != 0) { if (remaining < 16) { RDEBUG2(" Diameter attribute is too small to contain a Diameter header with Vendor-Id"); return 0; } hdr_len = 16; } /* * Get the length. If it's too big, die. */ length &= 0x00ffffff; /* * Too short or too long is bad. */ if (length <= (hdr_len - 4)) { RDEBUG2("Tunneled attribute %u is too short (%u < %u) to contain anything useful.", attr, length, hdr_len); return 0; } if (length > remaining) { RDEBUG2("Tunneled attribute %u is longer than room remaining in the packet (%u > %u).", attr, length, remaining); return 0; } /* * Check for broken implementations, which don't * pad the AVP to a 4-octet boundary. */ if (remaining == length) break; /* * The length does NOT include the padding, so * we've got to account for it here by rounding up * to the nearest 4-byte boundary. */ length += 0x03; length &= ~0x03; /* * If the rest of the diameter packet is larger than * this attribute, continue. * * Otherwise, if the attribute over-flows the end * of the packet, die. */ if (remaining < length) { RDEBUG2E("Diameter attribute overflows packet!"); return 0; } /* * remaining > length, continue. */ remaining -= length; data += length; } /* * We got this far. It looks OK. */ return 1; }
/* * Convert diameter attributes to our VALUE_PAIR's */ static VALUE_PAIR *diameter2vp(REQUEST *request, SSL *ssl, const uint8_t *data, size_t data_len) { uint32_t attr; uint32_t vendor; uint32_t length; size_t offset; size_t size; size_t data_left = data_len; VALUE_PAIR *first = NULL; VALUE_PAIR **last = &first; VALUE_PAIR *vp; while (data_left > 0) { rad_assert(data_left <= data_len); memcpy(&attr, data, sizeof(attr)); data += 4; attr = ntohl(attr); vendor = 0; memcpy(&length, data, sizeof(length)); data += 4; length = ntohl(length); /* * A "vendor" flag, with a vendor ID of zero, * is equivalent to no vendor. This is stupid. */ offset = 8; if ((length & (1 << 31)) != 0) { memcpy(&vendor, data, sizeof(vendor)); vendor = ntohl(vendor); data += 4; /* skip the vendor field, it's zero */ offset += 4; /* offset to value field */ if (attr > 65535) goto next_attr; if (vendor > FR_MAX_VENDOR) goto next_attr; } /* * FIXME: Handle the M bit. For now, we assume that * some other module takes care of any attribute * with the M bit set. */ /* * Get the length. */ length &= 0x00ffffff; /* * Get the size of the value portion of the * attribute. */ size = length - offset; /* * Vendor attributes can be larger than 255. * Normal attributes cannot be. */ if ((attr > 255) && (vendor == 0)) { RDEBUG2W("Skipping Diameter attribute %u", attr); goto next_attr; } /* * EAP-Message AVPs can be larger than 253 octets. */ if ((size > 253) && !((vendor == 0) && (attr == PW_EAP_MESSAGE))) { RDEBUG2W("diameter2vp skipping long attribute %u", attr); goto next_attr; } /* * RADIUS VSAs are handled as Diameter attributes * with Vendor-Id == 0, and the VSA data packed * into the "String" field as per normal. * * EXCEPT for the MS-CHAP attributes. */ if ((vendor == 0) && (attr == PW_VENDOR_SPECIFIC)) { ssize_t decoded; uint8_t buffer[256]; buffer[0] = PW_VENDOR_SPECIFIC; buffer[1] = size + 2; memcpy(buffer + 2, data, size); vp = NULL; decoded = rad_attr2vp(NULL, NULL, NULL, buffer, size + 2, &vp); if (decoded < 0) { RDEBUG2E("diameter2vp failed decoding attr: %s", fr_strerror()); goto do_octets; } if ((size_t) decoded != size + 2) { RDEBUG2E("diameter2vp failed to entirely decode VSA"); pairfree(&vp); goto do_octets; } *last = vp; do { last = &(vp->next); vp = vp->next; } while (vp != NULL); goto next_attr; } /* * Create it. If this fails, it's because we're OOM. */ do_octets: vp = paircreate(attr, vendor); if (!vp) { RDEBUG2("Failure in creating VP"); pairfree(&first); return NULL; } /* * If it's a type from our dictionary, then * we need to put the data in a relevant place. */ switch (vp->da->type) { case PW_TYPE_INTEGER: case PW_TYPE_DATE: if (size != vp->length) { const DICT_ATTR *da; /* * Bad format. Create a "raw" * attribute. */ raw: if (vp) pairfree(&vp); da = dict_attrunknown(attr, vendor, TRUE); if (!da) return NULL; vp = pairalloc(NULL, da); if (size >= 253) size = 253; vp->length = size; memcpy(vp->vp_octets, data, vp->length); break; } memcpy(&vp->vp_integer, data, vp->length); /* * Stored in host byte order: change it. */ vp->vp_integer = ntohl(vp->vp_integer); break; case PW_TYPE_INTEGER64: if (size != vp->length) goto raw; memcpy(&vp->vp_integer64, data, vp->length); /* * Stored in host byte order: change it. */ vp->vp_integer64 = ntohll(vp->vp_integer64); break; case PW_TYPE_IPADDR: if (size != vp->length) { RDEBUG2("Invalid length attribute %d", attr); pairfree(&first); pairfree(&vp); return NULL; } memcpy(&vp->vp_ipaddr, data, vp->length); /* * Stored in network byte order: don't change it. */ break; case PW_TYPE_BYTE: if (size != vp->length) goto raw; vp->vp_integer = data[0]; break; case PW_TYPE_SHORT: if (size != vp->length) goto raw; vp->vp_integer = (data[0] * 256) + data[1]; break; case PW_TYPE_SIGNED: if (size != vp->length) goto raw; memcpy(&vp->vp_signed, data, vp->length); vp->vp_signed = ntohl(vp->vp_signed); break; case PW_TYPE_IPV6ADDR: if (size != vp->length) goto raw; memcpy(&vp->vp_ipv6addr, data, vp->length); break; case PW_TYPE_IPV6PREFIX: if (size != vp->length) goto raw; memcpy(&vp->vp_ipv6prefix, data, vp->length); break; /* * String, octet, etc. Copy the data from the * value field over verbatim. */ case PW_TYPE_OCTETS: if (attr == PW_EAP_MESSAGE) { const uint8_t *eap_message = data; /* * vp exists the first time around. */ while (1) { vp->length = size; if (vp->length > 253) vp->length = 253; memcpy(vp->vp_octets, eap_message, vp->length); size -= vp->length; eap_message += vp->length; *last = vp; last = &(vp->next); if (size == 0) break; vp = paircreate(attr, vendor); if (!vp) { RDEBUG2("Failure in creating VP"); pairfree(&first); return NULL; } } goto next_attr; } /* else it's another kind of attribute */ /* FALL-THROUGH */ default: if (size >= 253) size = 253; vp->length = size; memcpy(vp->vp_strvalue, data, vp->length); break; } /* * User-Password is NUL padded to a multiple * of 16 bytes. Let's chop it to something * more reasonable. * * NOTE: This means that the User-Password * attribute CANNOT EVER have embedded zeros in it! */ if ((vp->da->vendor == 0) && (vp->da->attr == PW_USER_PASSWORD)) { /* * If the password is exactly 16 octets, * it won't be zero-terminated. */ vp->vp_strvalue[vp->length] = '\0'; vp->length = strlen(vp->vp_strvalue); } /* * Ensure that the client is using the * correct challenge. This weirdness is * to protect against against replay * attacks, where anyone observing the * CHAP exchange could pose as that user, * by simply choosing to use the same * challenge. * * By using a challenge based on * information from the current session, * we can guarantee that the client is * not *choosing* a challenge. * * We're a little forgiving in that we * have loose checks on the length, and * we do NOT check the Id (first octet of * the response to the challenge) * * But if the client gets the challenge correct, * we're not too worried about the Id. */ if (((vp->da->vendor == 0) && (vp->da->attr == PW_CHAP_CHALLENGE)) || ((vp->da->vendor == VENDORPEC_MICROSOFT) && (vp->da->attr == PW_MSCHAP_CHALLENGE))) { uint8_t challenge[16]; if ((vp->length < 8) || (vp->length > 16)) { RDEBUG("Tunneled challenge has invalid length"); pairfree(&first); pairfree(&vp); return NULL; } eapttls_gen_challenge(ssl, challenge, sizeof(challenge)); if (memcmp(challenge, vp->vp_octets, vp->length) != 0) { RDEBUG("Tunneled challenge is incorrect"); pairfree(&first); pairfree(&vp); return NULL; } } /* * Update the list. */ *last = vp; last = &(vp->next); next_attr: /* * Catch non-aligned attributes. */ if (data_left == length) break; /* * The length does NOT include the padding, so * we've got to account for it here by rounding up * to the nearest 4-byte boundary. */ length += 0x03; length &= ~0x03; rad_assert(data_left >= length); data_left -= length; data += length - offset; /* already updated */ } /* * We got this far. It looks OK. */ return first; }