return 1;
}
+static ssize_t eap_fast_decode_vp(TALLOC_CTX *request, DICT_ATTR const *parent,
+ uint8_t const *data, size_t const attr_len, VALUE_PAIR **out)
+{
+ int8_t tag = TAG_NONE;
+ VALUE_PAIR *vp;
+ uint8_t const *p = data;
+
+ /*
+ * FIXME: Attrlen can be larger than 253 for extended attrs!
+ */
+ if (!parent || !out ) {
+ RERROR("eap_fast_decode_vp: Invalid arguments");
+ return -1;
+ }
+
+ /*
+ * Silently ignore zero-length attributes.
+ */
+ if (attr_len == 0) return 0;
+
+ /*
+ * And now that we've verified the basic type
+ * information, decode the actual p.
+ */
+ vp = fr_pair_afrom_da(request, parent);
+ if (!vp) return -1;
+
+ vp->vp_length = attr_len;
+ vp->tag = tag;
+
+ switch (parent->type) {
+ case PW_TYPE_STRING:
+ fr_pair_value_bstrncpy(vp, p, attr_len);
+ break;
+
+ case PW_TYPE_OCTETS:
+ fr_pair_value_memcpy(vp, p, attr_len);
+ break;
+
+ case PW_TYPE_ABINARY:
+ if (vp->vp_length > sizeof(vp->vp_filter)) {
+ vp->vp_length = sizeof(vp->vp_filter);
+ }
+ memcpy(vp->vp_filter, p, vp->vp_length);
+ break;
+
+ case PW_TYPE_BYTE:
+ vp->vp_byte = p[0];
+ break;
+
+ case PW_TYPE_SHORT:
+ vp->vp_short = (p[0] << 8) | p[1];
+ break;
+
+ case PW_TYPE_INTEGER:
+ memcpy(&vp->vp_integer, p, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ break;
+
+ case PW_TYPE_INTEGER64:
+ memcpy(&vp->vp_integer64, p, 8);
+ vp->vp_integer64 = ntohll(vp->vp_integer64);
+ break;
+
+ case PW_TYPE_DATE:
+ memcpy(&vp->vp_date, p, 4);
+ vp->vp_date = ntohl(vp->vp_date);
+ break;
+
+ case PW_TYPE_ETHERNET:
+ memcpy(vp->vp_ether, p, 6);
+ break;
+
+ case PW_TYPE_IPV4_ADDR:
+ memcpy(&vp->vp_ipaddr, p, 4);
+ break;
+
+ case PW_TYPE_IFID:
+ memcpy(vp->vp_ifid, p, 8);
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ memcpy(&vp->vp_ipv6addr, p, 16);
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ /*
+ * FIXME: double-check that
+ * (vp->vp_octets[1] >> 3) matches vp->vp_length + 2
+ */
+ memcpy(vp->vp_ipv6prefix, p, vp->vp_length);
+ if (vp->vp_length < 18) {
+ memset(((uint8_t *)vp->vp_ipv6prefix) + vp->vp_length, 0,
+ 18 - vp->vp_length);
+ }
+ break;
+
+ case PW_TYPE_IPV4_PREFIX:
+ /* FIXME: do the same double-check as for IPv6Prefix */
+ memcpy(vp->vp_ipv4prefix, p, vp->vp_length);
+
+ /*
+ * /32 means "keep all bits". Otherwise, mask
+ * them out.
+ */
+ if ((p[1] & 0x3f) > 32) {
+ uint32_t addr, mask;
+
+ memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
+ mask = 1;
+ mask <<= (32 - (p[1] & 0x3f));
+ mask--;
+ mask = ~mask;
+ mask = htonl(mask);
+ addr &= mask;
+ memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
+ }
+ break;
+
+ case PW_TYPE_SIGNED: /* overloaded with vp_integer */
+ memcpy(&vp->vp_integer, p, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ break;
+
+ default:
+ RERROR("eap_fast_decode_vp: type %d Internal sanity check %d ", parent->type, __LINE__);
+ fr_pair_list_free(&vp);
+ return -1;
+ }
+ vp->type = VT_DATA;
+ *out = vp;
+ return attr_len;
+}
+
VALUE_PAIR *eap_fast_fast2vp(REQUEST *request, SSL *ssl, uint8_t const *data, size_t data_len,
DICT_ATTR const *fast_da, vp_cursor_t *out)
DICT_ATTR const *da;
if (!fast_da)
- fast_da = dict_attrbyvalue(0, PW_EAP_FAST_TLV);
+ fast_da = dict_attrbyvalue(PW_FREERADIUS_EAP_FAST_TLV, VENDORPEC_FREERADIUS);
rad_assert(fast_da != NULL);
if (!out) {
*
* For now, if it doesn't exist, ignore it.
*/
- da = dict_attrbyparent(fast_da, attr, 0);
- if (!da) goto next_attr;
-
+ da = dict_attrbyparent(fast_da, attr, fast_da->vendor);
+ if (!da) {
+ RDEBUG("eap_fast_fast2vp: no sub attribute found %s attr: %u vendor: %u",
+ fast_da->name, attr, fast_da->vendor);
+ goto next_attr;
+ }
if (da->type == PW_TYPE_TLV) {
eap_fast_fast2vp(request, ssl, data, length, da, out);
goto next_attr;
}
-/*
-ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
- uint8_t const *data, size_t const attr_len, size_t const packet_len,
- void *decoder_ctx)
-
- vp = NULL;
- decoded = rad_attr2vp(request->packet, NULL, NULL, NULL,
- data, size + 2, &vp);
+ decoded = eap_fast_decode_vp(request, da, data, length, &vp);
if (decoded < 0) {
RERROR("Failed decoding %s: %s", da->name, fr_strerror());
goto next_attr;
}
-*/
+
+ fr_cursor_merge(out, vp);
next_attr:
while (fr_cursor_next(out)) {
}
}
+static void eapfast_copy_request_to_tunnel(REQUEST *request, REQUEST *fake) {
+ VALUE_PAIR *copy, *vp;
+ vp_cursor_t cursor;
+
+ for (vp = fr_cursor_init(&cursor, &request->packet->vps);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ /*
+ * The attribute is a server-side thingy,
+ * don't copy it.
+ */
+ if ((vp->da->attr > 255) && (((vp->da->attr >> 16) & 0xffff) == 0)) {
+ continue;
+ }
+
+ /*
+ * The outside attribute is already in the
+ * tunnel, don't copy it.
+ *
+ * This works for BOTH attributes which
+ * are originally in the tunneled request,
+ * AND attributes which are copied there
+ * from below.
+ */
+ if (fr_pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) continue;
+
+ /*
+ * Some attributes are handled specially.
+ */
+ if (!vp->da->vendor) switch (vp->da->attr) {
+ /*
+ * NEVER copy Message-Authenticator,
+ * EAP-Message, or State. They're
+ * only for outside of the tunnel.
+ */
+ case PW_USER_NAME:
+ case PW_USER_PASSWORD:
+ case PW_CHAP_PASSWORD:
+ case PW_CHAP_CHALLENGE:
+ case PW_PROXY_STATE:
+ case PW_MESSAGE_AUTHENTICATOR:
+ case PW_EAP_MESSAGE:
+ case PW_STATE:
+ continue;
+
+ /*
+ * By default, copy it over.
+ */
+ default:
+ break;
+ }
+
+ /*
+ * Don't copy from the head, we've already
+ * checked it.
+ */
+ copy = fr_pair_list_copy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
+ fr_pair_add(&fake->packet->vps, copy);
+ }
+}
/*
* Use a reply packet to determine what to do.
*/
fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_REPLY_MESSAGE, 0, TAG_ANY);
+ if (vp) {
+ RDEBUG("Sending tunneled reply attributes");
+ eap_vp2fast(tls_session, vp);
+ fr_pair_list_free(&vp);
+ }
+
rcode = RLM_MODULE_HANDLED;
break;
* Add the tunneled attributes to the fake request.
*/
- fake->packet->vps = fr_pair_afrom_num(fake->packet, 0, PW_EAP_MESSAGE);
+ fake->packet->vps = fr_pair_afrom_num(fake->packet, PW_EAP_MESSAGE, 0);
fr_pair_value_memcpy(fake->packet->vps, tlv_eap_payload->vp_octets, tlv_eap_payload->vp_length);
RDEBUG("Got tunneled request");
/*
* Update other items in the REQUEST data structure.
*/
- fake->username = fr_pair_find_by_num(fake->packet->vps, 0, PW_USER_NAME, TAG_ANY);
- fake->password = fr_pair_find_by_num(fake->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY);
+ fake->username = fr_pair_find_by_num(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ fake->password = fr_pair_find_by_num(fake->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
/*
* No User-Name, try to create one from stored data.
* an EAP-Identity, and pull it out of there.
*/
if (!t->username) {
- vp = fr_pair_find_by_num(fake->packet->vps, 0, PW_EAP_MESSAGE, TAG_ANY);
+ vp = fr_pair_find_by_num(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
if (vp &&
(vp->vp_length >= EAP_HEADER_LEN + 2) &&
(vp->vp_strvalue[0] == PW_EAP_RESPONSE) &&
if (t->username) {
vp = fr_pair_list_copy(fake->packet, t->username);
fr_pair_add(&fake->packet->vps, vp);
- fake->username = fr_pair_find_by_num(fake->packet->vps, 0, PW_USER_NAME, TAG_ANY);
+ fake->username = vp;
}
} /* else the request ALREADY had a User-Name */
+ /*
+ * Add the State attribute, too, if it exists.
+ */
+ if (t->state) {
+ vp = fr_pair_list_copy(fake->packet, t->state);
+ if (vp) fr_pair_add(&fake->packet->vps, vp);
+ }
+
+
if (t->stage == AUTHENTICATION) { /* FIXME do this only for MSCHAPv2 */
VALUE_PAIR *tvp;
- tvp = fr_pair_afrom_num(fake->packet, 0, PW_EAP_TYPE);
- tvp->vp_integer = t->default_provisioning_method;
- fr_pair_add(&fake->config, tvp);
+ RWDEBUG2("AUTHENTICATION");
+ vp = fr_pair_make(fake, &fake->config, "EAP-Type", "0", T_OP_EQ);
+ vp->vp_integer = t->default_method;
+ RWDEBUG2("AUTHENTICATION");
/*
* RFC 5422 section 3.2.3 - Authenticating Using EAP-FAST-MSCHAPv2
*/
if (t->mode == EAP_FAST_PROVISIONING_ANON) {
- tvp = fr_pair_afrom_num(fake->packet, VENDORPEC_MICROSOFT, PW_MSCHAP_CHALLENGE);
+ tvp = fr_pair_afrom_num(fake->packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
fr_pair_value_memcpy(tvp, t->keyblock->server_challenge, CHAP_VALUE_LENGTH);
fr_pair_add(&fake->config, tvp);
- tvp = fr_pair_afrom_num(fake->packet, 0, PW_MS_CHAP_PEER_CHALLENGE);
+ tvp = fr_pair_afrom_num(fake->packet, PW_MS_CHAP_PEER_CHALLENGE, 0);
fr_pair_value_memcpy(tvp, t->keyblock->client_challenge, CHAP_VALUE_LENGTH);
fr_pair_add(&fake->config, tvp);
}
}
+ if (t->copy_request_to_tunnel) {
+ eapfast_copy_request_to_tunnel(request, fake);
+ }
+
+ if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
+ fake->server = vp->vp_strvalue;
+
+ } else if (t->virtual_server) {
+ fake->server = t->virtual_server;
+
+ } /* else fake->server == request->server */
+
/*
* Call authentication recursively, which will
* do PAP, CHAP, MS-CHAP, etc.
switch (fake->reply->code) {
case 0: /* No reply code, must be proxied... */
#ifdef WITH_PROXY
- vp = fr_pair_find_by_num(fake->config, 0, PW_PROXY_TO_REALM, TAG_ANY);
+ vp = fr_pair_find_by_num(fake->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
if (vp) {
int ret;
eap_tunnel_data_t *tunnel;
* Tell the original request that it's going
* to be proxied.
*/
- fr_pair_list_mcopy_by_num(request, &request->config, &fake->config, 0,
- PW_PROXY_TO_REALM, TAG_ANY);
+ fr_pair_list_mcopy_by_num(request, &request->config, &fake->config, PW_PROXY_TO_REALM, 0,
+ TAG_ANY);
/*
* Seed the proxy packet with the
return PW_CODE_ACCESS_ACCEPT;
}
+
+#define PW_EAP_FAST_TLV_PAC (PW_FREERADIUS_EAP_FAST_TLV | (EAP_FAST_TLV_PAC << 8))
+
+
+
static PW_CODE eap_fast_process_tlvs(REQUEST *request, eap_handler_t *eap_session,
tls_session_t *tls_session, VALUE_PAIR *fast_vps)
{
for (vp = fr_cursor_init(&cursor, &fast_vps); vp; vp = fr_cursor_next(&cursor)) {
PW_CODE code = PW_CODE_ACCESS_REJECT;
char *value;
- DICT_ATTR * parent = dict_parent(vp->da->attr, vp->da->vendor);
+ DICT_ATTR const *parent_da = NULL;
+ parent_da = dict_parent(vp->da->attr, vp->da->vendor);
+ if (parent_da == NULL || vp->da->vendor != VENDORPEC_FREERADIUS ||
+ ((vp->da->attr & 0xff) != PW_FREERADIUS_EAP_FAST_TLV)) {
+ value = vp_aprints(request->packet, vp, '"');
+ RDEBUG2("ignoring non-EAP-FAST TLV %s", value);
+ talloc_free(value);
+ continue;
+ }
- switch (parent->attr) {
- case PW_EAP_FAST_TLV:
- switch (vp->da->attr) {
+ switch (parent_da->attr) {
+ case PW_FREERADIUS_EAP_FAST_TLV:
+ switch (vp->da->attr >> 8) {
case EAP_FAST_TLV_EAP_PAYLOAD:
code = eap_fast_eap_payload(request, eap_session, tls_session, vp);
if (code == PW_CODE_ACCESS_ACCEPT)
code = PW_CODE_ACCESS_ACCEPT;
t->stage = PROVISIONING;
break;
+ case EAP_FAST_TLV_CRYPTO_BINDING:
+ if (!binding) {
+ binding = talloc_zero(request->packet, eap_tlv_crypto_binding_tlv_t);
+ memcpy(binding, vp->vp_octets, sizeof(*binding));
+ binding->tlv_type = htons(EAP_FAST_TLV_MANDATORY | EAP_FAST_TLV_CRYPTO_BINDING);
+ binding->length = htons(sizeof(*binding) - 2 * sizeof(uint16_t));
+ }
+ continue;
default:
value = vp_aprints_value(request->packet, vp, '"');
RDEBUG2("ignoring unknown %s", value);
continue;
}
break;
- case EAP_FAST_TLV_CRYPTO_BINDING:
- if (!binding) {
- binding = talloc_zero(request->packet, eap_tlv_crypto_binding_tlv_t);
- binding->tlv_type = htons(EAP_FAST_TLV_MANDATORY | EAP_FAST_TLV_CRYPTO_BINDING);
- binding->length = htons(sizeof(*binding) - 2 * sizeof(uint16_t));
- }
- /*
- * fr_radius_encode_pair() does not work for structures
- */
- switch (vp->da->attr) {
- case 1: /* PW_EAP_FAST_CRYPTO_BINDING_RESERVED */
- binding->reserved = vp->vp_integer;
- break;
- case 2: /* PW_EAP_FAST_CRYPTO_BINDING_VERSION */
- binding->version = vp->vp_integer;
- break;
- case 3: /* PW_EAP_FAST_CRYPTO_BINDING_RECV_VERSION */
- binding->received_version = vp->vp_integer;
- break;
- case 4: /* PW_EAP_FAST_CRYPTO_BINDING_SUB_TYPE */
- binding->subtype = vp->vp_integer;
- break;
- case 5: /* PW_EAP_FAST_CRYPTO_BINDING_NONCE */
- memcpy(binding->nonce, vp->vp_octets, vp->vp_length);
- break;
- case 6: /* PW_EAP_FAST_CRYPTO_BINDING_COMPOUND_MAC */
- memcpy(binding->compound_mac, vp->vp_octets, vp->vp_length);
- break;
- }
- continue;
- case EAP_FAST_TLV_PAC:
- switch (vp->da->attr) {
+ case PW_EAP_FAST_TLV_PAC:
+ switch ( ( vp->da->attr >> 16 )) {
case PAC_INFO_PAC_ACK:
if (vp->vp_integer == EAP_FAST_TLV_RESULT_SUCCESS) {
code = PW_CODE_ACCESS_ACCEPT;
t->pac.send = true;
continue;
default:
- value = vp_aprints_value(request->packet, vp, '"');
+ value = vp_aprints(request->packet, vp, '"');
RDEBUG2("ignoring unknown EAP-FAST-PAC-TLV %s", value);
talloc_free(value);
continue;
}
break;
default:
- value = vp_aprints_value(request->packet, vp, '"');
- RDEBUG2("ignoring non-EAP-FAST TLV %s", value);
+ value = vp_aprints(request->packet, vp, '"');
+ RDEBUG2("ignoring EAP-FAST TLV %s", value);
talloc_free(value);
continue;
}
/*
* See if the tunneled data is well formed.
*/
- if (!eap_fast_verify(request, tls_session, data, data_len)) return RLM_MODULE_REJECT;
+ if (!eap_fast_verify(request, tls_session, data, data_len)) return PW_CODE_ACCESS_REJECT;
if (t->stage == TLS_SESSION_HANDSHAKE) {
rad_assert(t->mode == EAP_FAST_UNKNOWN);
fr_pair_list_free(&fast_vps);
- if (code == RLM_MODULE_REJECT) return RLM_MODULE_REJECT;
+ if (code == PW_CODE_ACCESS_REJECT) return PW_CODE_ACCESS_REJECT;
switch (t->stage) {
case AUTHENTICATION:
eap_fast_append_result(tls_session, code);
- if (code == RLM_MODULE_REJECT)
+ if (code == PW_CODE_ACCESS_REJECT)
break;
if (t->pac.send) {
/*
* RFC 5422 section 3.5 - Network Access after EAP-FAST Provisioning
*/
- if ((t->pac.type && t->pac.expired) || t->mode == EAP_FAST_PROVISIONING_ANON) {
- RDEBUG("Rejecting expired PAC or unauthenticated provisioning");
- code = RLM_MODULE_REJECT;
+ if (t->pac.type && t->pac.expired) {
+ REDEBUG("Rejecting expired PAC.");
+ code = PW_CODE_ACCESS_REJECT;
+ break;
+ }
+
+ if (t->mode == EAP_FAST_PROVISIONING_ANON) {
+ REDEBUG("Rejecting unauthenticated provisioning");
+ code = PW_CODE_ACCESS_REJECT;
break;
}
eap_add_reply(request, "EAP-EMSK", t->emsk, EAP_EMSK_LEN);
break;
+
default:
- RERROR("no idea! %d", t->stage);
- code = RLM_MODULE_REJECT;
+ RERROR("Internal sanity check failed in EAP-FAST at %d", t->stage);
+ code = PW_CODE_ACCESS_REJECT;
}
return code;