* Copyright 2006 The FreeRADIUS server project
*/
-#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include "eap_ttls.h"
/*
* Verify that the diameter packet is valid.
*/
-static int diameter_verify(REQUEST *request,
- const uint8_t *data, unsigned int data_len)
+static int diameter_verify(REQUEST *request, uint8_t const *data, unsigned int data_len)
{
uint32_t attr;
uint32_t length;
- unsigned int offset;
- unsigned int data_left = data_len;
+ unsigned int hdr_len;
+ unsigned int remaining = data_len;
- while (data_left > 0) {
- if (data_len < 12) {
- RDEBUG2(" Diameter attribute is too small to contain a Diameter header");
+ 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;
}
- rad_assert(data_left <= data_len);
memcpy(&attr, data, sizeof(attr));
- data += 4;
attr = ntohl(attr);
- if (attr > 255) {
- RDEBUG2(" Non-RADIUS attribute in tunneled authentication is not supported");
- return 0;
- }
-
- memcpy(&length, data , sizeof(length));
- data += 4;
+ memcpy(&length, data + 4, sizeof(length));
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) {
- int attribute;
- uint32_t vendor;
- DICT_ATTR *da;
-
- memcpy(&vendor, data, sizeof(vendor));
- vendor = ntohl(vendor);
-
- if (vendor > 65535) {
- RDEBUG2("Vendor codes larger than 65535 are not supported");
- return 0;
- }
-
- attribute = (vendor << 16) | attr;
-
- da = dict_attrbyvalue(attribute);
-
- /*
- * SHOULD check ((length & (1 << 30)) != 0)
- * for the mandatory bit.
- */
- if (!da) {
- RDEBUG2("Fatal! Vendor %u, Attribute %u was not found in our dictionary. ",
- vendor, attr);
+ if ((data[4] & 0x80) != 0) {
+ if (remaining < 16) {
+ RDEBUG2("Diameter attribute is too small to contain a Diameter header with Vendor-Id");
return 0;
}
- data += 4; /* skip the vendor field */
- offset += 4; /* offset to value field */
+ hdr_len = 16;
}
/*
- * Ignore the M bit. We support all RADIUS attributes...
- */
-
- /*
* Get the length. If it's too big, die.
*/
length &= 0x00ffffff;
/*
* Too short or too long is bad.
*/
- if (length < offset) {
- RDEBUG2("Tunneled attribute %d is too short (%d)to contain anything useful.", attr, length);
- return 0;
- }
-
- /*
- * EAP Messages cane be longer than MAX_STRING_LEN.
- * Other attributes cannot be.
- */
- if ((attr != PW_EAP_MESSAGE) &&
- (length > (MAX_STRING_LEN + 8))) {
- RDEBUG2("Tunneled attribute %d is too long (%d) to pack into a RADIUS attribute.", attr, length);
+ 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 > data_left) {
- RDEBUG2("Tunneled attribute %d is longer than room left in the packet (%d > %d).", attr, length, data_left);
+ 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 (data_left == length) break;
+ if (remaining == length) break;
/*
* The length does NOT include the padding, so
* Otherwise, if the attribute over-flows the end
* of the packet, die.
*/
- if (data_left < length) {
- RDEBUG2("ERROR! Diameter attribute overflows packet!");
+ if (remaining < length) {
+ REDEBUG2("Diameter attribute overflows packet!");
return 0;
}
/*
- * Check again for equality, now that we're padded
- * length to a multiple of 4 octets.
- */
- if (data_left == length) break;
-
- /*
- * data_left > length, continue.
+ * remaining > length, continue.
*/
- data_left -= length;
- data += length - offset;
+ remaining -= length;
+ data += length;
}
/*
/*
* Convert diameter attributes to our VALUE_PAIR's
*/
-static VALUE_PAIR *diameter2vp(REQUEST *request, SSL *ssl,
- const uint8_t *data, size_t data_len)
+static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
+ uint8_t const *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;
+ char *p;
VALUE_PAIR *first = NULL;
- VALUE_PAIR **last = &first;
VALUE_PAIR *vp;
+ RADIUS_PACKET *packet = fake->packet; /* FIXME: api issues */
+ vp_cursor_t out;
+
+ fr_cursor_init(&out, &first);
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;
*/
offset = 8;
if ((length & (1 << 31)) != 0) {
- uint32_t vendor;
-
memcpy(&vendor, data, sizeof(vendor));
vendor = ntohl(vendor);
- if (attr > 65535) {
- RDEBUG2("Cannot handle vendor attributes greater than 65535");
- pairfree(&first);
- return NULL;
- }
-
- if (vendor > 32767) {
- RDEBUG2("Cannot handle vendor Id greater than 32767");
- pairfree(&first);
- return NULL;
- }
-
- attr |= (vendor << 16);
-
data += 4; /* skip the vendor field, it's zero */
offset += 4; /* offset to value field */
- }
- /*
- * Vendor attributes can be larger than 255.
- * Normal attributes cannot be.
- */
- if ((attr > 255) && (VENDOR(attr) == 0)) {
- RDEBUG2("Cannot handle Diameter attributes");
- pairfree(&first);
- return NULL;
+ if (attr > 65535) goto next_attr;
+ if (vendor > FR_MAX_VENDOR) goto next_attr;
}
/*
* some other module takes care of any attribute
* with the M bit set.
*/
-
+
/*
* Get the length.
*/
size = length - offset;
/*
- * Create it.
+ * Vendor attributes can be larger than 255.
+ * Normal attributes cannot be.
*/
- vp = paircreate(attr, PW_TYPE_OCTETS);
+ if ((attr > 255) && (vendor == 0)) {
+ RWDEBUG2("Skipping Diameter attribute %u", attr);
+ goto next_attr;
+ }
+
+ /*
+ * EAP-Message AVPs can be larger than 253 octets.
+ *
+ * For now, we rely on the main decoder in
+ * src/lib/radius to decode data into VPs. This
+ * means putting the data into a RADIUS attribute
+ * format. It also means that we can't handle
+ * "extended" attributes in the Diameter space. Oh well...
+ */
+ if ((size > 253) && !((vendor == 0) && (attr == PW_EAP_MESSAGE))) {
+ RWDEBUG2("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(packet, NULL, NULL, NULL,
+ buffer, size + 2, &vp);
+ if (decoded < 0) {
+ REDEBUG2("diameter2vp failed decoding attr: %s",
+ fr_strerror());
+ goto do_octets;
+ }
+
+ if ((size_t) decoded != size + 2) {
+ REDEBUG2("diameter2vp failed to entirely decode VSA");
+ pairfree(&vp);
+ goto do_octets;
+ }
+
+ fr_cursor_insert(&out, vp);
+
+ goto next_attr;
+ }
+
+ /*
+ * Create it. If this fails, it's because we're OOM.
+ */
+ do_octets:
+ vp = paircreate(packet, attr, vendor);
if (!vp) {
RDEBUG2("Failure in creating VP");
pairfree(&first);
/*
* If it's a type from our dictionary, then
* we need to put the data in a relevant place.
+ *
+ * @todo: Export the lib/radius.c decoder, and use it here!
*/
- switch (vp->type) {
+ switch (vp->da->type) {
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
if (size != vp->length) {
- RDEBUG2("Invalid length attribute %d",
- attr);
- pairfree(&first);
- pairfree(&vp);
- return NULL;
+ DICT_ATTR const *da;
+
+ /*
+ * Bad format. Create a "raw"
+ * attribute.
+ */
+ raw:
+ if (vp) pairfree(&vp);
+ da = dict_attrunknown(attr, vendor, true);
+ if (!da) return NULL;
+ vp = pairalloc(packet, da);
+ if (!vp) return NULL;
+ pairmemcpy(vp, data, size);
+ break;
}
memcpy(&vp->vp_integer, data, vp->length);
vp->vp_integer = ntohl(vp->vp_integer);
break;
- case PW_TYPE_IPADDR:
+ 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_IPV4_ADDR:
if (size != vp->length) {
RDEBUG2("Invalid length attribute %d",
attr);
pairfree(&vp);
return NULL;
}
- memcpy(&vp->vp_ipaddr, data, vp->length);
-
- /*
- * Stored in network byte order: don't change it.
- */
- break;
-
- /*
- * String, octet, etc. Copy the data from the
- * value field over verbatim.
- *
- * FIXME: Ipv6 attributes ?
- *
- */
- case PW_TYPE_OCTETS:
- if (attr == PW_EAP_MESSAGE) {
- const uint8_t *eap_message = data;
+ memcpy(&vp->vp_ipaddr, data, vp->length);
- /*
- * 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, PW_TYPE_OCTETS);
- if (!vp) {
- RDEBUG2("Failure in creating VP");
- pairfree(&first);
- return NULL;
- }
- }
+ /*
+ * Stored in network byte order: don't change it.
+ */
+ break;
- goto next_attr;
- } /* else it's another kind of attribute */
- /* FALL-THROUGH */
+ case PW_TYPE_BYTE:
+ if (size != vp->length) goto raw;
+ vp->vp_integer = data[0];
+ break;
- default:
- vp->length = size;
- memcpy(vp->vp_strvalue, data, vp->length);
+ 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_IPV6_ADDR:
+ if (size != vp->length) goto raw;
+ memcpy(&vp->vp_ipv6addr, data, vp->length);
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if (size != vp->length) goto raw;
+ memcpy(&vp->vp_ipv6prefix, 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!
- */
- switch (vp->attribute) {
- case PW_USER_PASSWORD:
/*
- * If the password is exactly 16 octets,
- * it won't be zero-terminated.
+ * Ensure it's NUL terminated.
*/
- vp->vp_strvalue[vp->length] = '\0';
- vp->length = strlen(vp->vp_strvalue);
+ case PW_TYPE_STRING:
+ vp->vp_strvalue = p = talloc_array(vp, char, size + 1);
+ vp->type = VT_DATA;
+ memcpy(p, data, size);
+ p[size] = '\0';
+ vp->length = strlen(p);
break;
/*
- * 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.
+ * Copy it over verbatim.
*/
- case PW_CHAP_CHALLENGE:
- case PW_MSCHAP_CHALLENGE:
+ case PW_TYPE_OCTETS:
+ default:
+ pairmemcpy(vp, data, size);
+ break;
+ }
+
+ /*
+ * 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;
+ }
- } else {
- uint8_t challenge[16];
-
- eapttls_gen_challenge(ssl, challenge,
- sizeof(challenge));
+ 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;
- }
+ if (memcmp(challenge, vp->vp_octets,
+ vp->length) != 0) {
+ RDEBUG("Tunneled challenge is incorrect");
+ pairfree(&first);
+ pairfree(&vp);
+ return NULL;
}
- break;
-
- default:
- break;
- } /* switch over checking/re-writing of attributes. */
+ }
/*
* Update the list.
*/
- *last = vp;
- last = &(vp->next);
+ fr_cursor_insert(&out, vp);
next_attr:
/*
uint32_t length;
uint32_t vendor;
size_t total;
+ uint64_t attr64;
VALUE_PAIR *vp;
+ vp_cursor_t cursor;
p = buffer;
total = 0;
- for (vp = first; vp != NULL; vp = vp->next) {
+ for (vp = fr_cursor_init(&cursor, &first); vp; vp = fr_cursor_next(&cursor)) {
/*
* Too much data: die.
*/
* together. Maybe we should...
*/
- /*
- * Length is no more than 253, due to RADIUS
- * issues.
- */
length = vp->length;
- vendor = (vp->attribute >> 16) & 0xffff;
+ vendor = vp->da->vendor;
if (vendor != 0) {
- attr = vp->attribute & 0xffff;
+ attr = vp->da->attr & 0xffff;
length |= (1 << 31);
} else {
- attr = vp->attribute;
+ attr = vp->da->attr;
}
/*
total += 4;
}
- switch (vp->type) {
+ switch (vp->da->type) {
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
- attr = ntohl(vp->vp_integer); /* stored in host order */
+ attr = htonl(vp->vp_integer); /* stored in host order */
memcpy(p, &attr, sizeof(attr));
length = 4;
break;
- case PW_TYPE_IPADDR:
+ case PW_TYPE_INTEGER64:
+ attr64 = htonll(vp->vp_integer64); /* stored in host order */
+ memcpy(p, &attr64, sizeof(attr64));
+ length = 8;
+ break;
+
+ case PW_TYPE_IPV4_ADDR:
memcpy(p, &vp->vp_ipaddr, 4); /* network order */
length = 4;
break;
if ((debug_flag > 2) && fr_log_fp) {
for (i = 0; i < total; i++) {
- if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TTLS tunnel data out %04x: ", i);
+ if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TTLS tunnel data out %04x: ", (int) i);
fprintf(fr_log_fp, "%02x ", buffer[i]);
/*
* FIXME: Check the return code.
*/
- tls_handshake_send(tls_session);
+ tls_handshake_send(request, tls_session);
}
/*
/*
* Use a reply packet to determine what to do.
*/
-static int process_reply(EAP_HANDLER *handler, tls_session_t *tls_session,
- REQUEST *request, RADIUS_PACKET *reply)
+static rlm_rcode_t CC_HINT(nonnull) process_reply(UNUSED eap_handler_t *handler, tls_session_t *tls_session,
+ REQUEST *request, RADIUS_PACKET *reply)
{
- int rcode = RLM_MODULE_REJECT;
+ rlm_rcode_t rcode = RLM_MODULE_REJECT;
VALUE_PAIR *vp;
ttls_tunnel_t *t = tls_session->opaque;
- handler = handler; /* -Wunused */
+ rad_assert(handler->request == request);
/*
* If the response packet was Access-Accept, then
* NOT 'eap start', so we should check for that....
*/
switch (reply->code) {
- case PW_AUTHENTICATION_ACK:
+ case PW_CODE_ACCESS_ACCEPT:
RDEBUG("Got tunneled Access-Accept");
rcode = RLM_MODULE_OK;
* packet, and we will send EAP-Success.
*/
vp = NULL;
- pairmove2(&vp, &reply->vps, PW_MSCHAP2_SUCCESS);
+ pairfilter(tls_session, &vp, &reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
if (vp) {
- RDEBUG("Got MS-CHAP2-Success, tunneling it to the client in a challenge.");
+ RDEBUG("Got MS-CHAP2-Success, tunneling it to the client in a challenge");
rcode = RLM_MODULE_HANDLED;
- t->authenticated = TRUE;
+ t->authenticated = true;
/*
* Delete MPPE keys & encryption policy. We don't
* want these here.
*/
- pairdelete(&reply->vps, ((311 << 16) | 7));
- pairdelete(&reply->vps, ((311 << 16) | 8));
- pairdelete(&reply->vps, ((311 << 16) | 16));
- pairdelete(&reply->vps, ((311 << 16) | 17));
+ pairdelete(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
+ pairdelete(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
+ pairdelete(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
+ pairdelete(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
/*
* Use the tunneled reply, but not now.
*/
if (t->use_tunneled_reply) {
- t->reply = reply->vps;
- reply->vps = NULL;
+ rad_assert(!t->accept_vps);
+ pairfilter(t, &t->accept_vps, &reply->vps,
+ 0, 0, TAG_ANY);
+ rad_assert(!reply->vps);
}
} else { /* no MS-CHAP2-Success */
* can figure it out, from the non-tunneled
* EAP-Success packet.
*/
- pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE);
+ pairfilter(tls_session, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
pairfree(&vp);
}
* tunneled user!
*/
if (t->use_tunneled_reply) {
- pairdelete(&reply->vps, PW_PROXY_STATE);
- pairadd(&request->reply->vps, reply->vps);
- reply->vps = NULL;
+ pairdelete(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+ pairfilter(request->reply, &request->reply->vps,
+ &reply->vps, 0, 0, TAG_ANY);
}
break;
- case PW_AUTHENTICATION_REJECT:
+ case PW_CODE_ACCESS_REJECT:
RDEBUG("Got tunneled Access-Reject");
rcode = RLM_MODULE_REJECT;
break;
* an Access-Challenge means that we MUST tunnel
* a Reply-Message to the client.
*/
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_CHALLENGE:
RDEBUG("Got tunneled Access-Challenge");
/*
* Get rid of the old State, too.
*/
pairfree(&t->state);
- pairmove2(&t->state, &reply->vps, PW_STATE);
+ pairfilter(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY);
/*
* We should really be a bit smarter about this,
* method works in 99.9% of the situations.
*/
vp = NULL;
- pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE);
+ pairfilter(t, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
/*
* There MUST be a Reply-Message in the challenge,
* we MUST create one, with an empty string as
* it's value.
*/
- pairmove2(&vp, &reply->vps, PW_REPLY_MESSAGE);
+ pairfilter(t, &vp, &reply->vps, PW_REPLY_MESSAGE, 0, TAG_ANY);
/*
* Handle the ACK, by tunneling any necessary reply
}
+#ifdef WITH_PROXY
/*
* Do post-proxy processing,
*/
-static int eapttls_postproxy(EAP_HANDLER *handler, void *data)
+static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data)
{
int rcode;
tls_session_t *tls_session = (tls_session_t *) data;
REQUEST *fake, *request = handler->request;
- RDEBUG("Passing reply from proxy back into the tunnel.");
+ RDEBUG("Passing reply from proxy back into the tunnel");
/*
* If there was a fake request associated with the proxied
/*
* Do the callback, if it exists, and if it was a success.
*/
- if (fake && (handler->request->proxy_reply->code == PW_AUTHENTICATION_ACK)) {
- REQUEST *request = handler->request;
-
+ if (fake && (handler->request->proxy_reply->code == PW_CODE_ACCESS_ACCEPT)) {
/*
* Terrible hacks.
*/
- rad_assert(fake->packet == NULL);
- fake->packet = request->proxy;
+ rad_assert(!fake->packet);
+ fake->packet = talloc_steal(fake, request->proxy);
fake->packet->src_ipaddr = request->packet->src_ipaddr;
request->proxy = NULL;
- rad_assert(fake->reply == NULL);
- fake->reply = request->proxy_reply;
+ rad_assert(!fake->reply);
+ fake->reply = talloc_steal(fake, request->proxy_reply);
request->proxy_reply = NULL;
if ((debug_flag > 0) && fr_log_fp) {
- fprintf(fr_log_fp, "server %s {\n", fake->server);
+ fprintf(fr_log_fp, "server %s {\n",
+ (!fake->server) ? "" : fake->server);
}
/*
* Perform a post-auth stage for the tunneled
* session.
*/
- fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+ fake->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
rcode = rad_postauth(fake);
RDEBUG2("post-auth returns %d", rcode);
if ((debug_flag > 0) && fr_log_fp) {
- fprintf(fr_log_fp, "} # server %s\n", fake->server);
-
+ fprintf(fr_log_fp, "} # server %s\n",
+ (!fake->server) ? "" : fake->server);
+
RDEBUG("Final reply from tunneled session code %d",
fake->reply->code);
debug_pair_list(fake->reply->vps);
/*
* Terrible hacks.
*/
- request->proxy = fake->packet;
+ request->proxy = talloc_steal(request, fake->packet);
fake->packet = NULL;
- request->proxy_reply = fake->reply;
+ request->proxy_reply = talloc_steal(request, fake->reply);
fake->reply = NULL;
/*
*/
switch (rcode) {
- case RLM_MODULE_FAIL:
- request_free(&fake);
+ case RLM_MODULE_FAIL:
+ talloc_free(fake);
eaptls_fail(handler, 0);
return 0;
- break;
- default: /* Don't Do Anything */
- RDEBUG2("sGot reply %d",
+ default: /* Don't Do Anything */
+ RDEBUG2("Got reply %d",
request->proxy_reply->code);
break;
}
}
- request_free(&fake); /* robust if fake == NULL */
+ talloc_free(fake); /* robust if !fake */
/*
* Process the reply from the home server.
*/
- rcode = process_reply(handler, tls_session, handler->request,
- handler->request->proxy_reply);
+ rcode = process_reply(handler, tls_session, handler->request, handler->request->proxy_reply);
/*
* The proxy code uses the reply from the home server as
case RLM_MODULE_HANDLED:
RDEBUG("Reply was handled");
eaptls_request(handler->eap_ds, tls_session);
+ request->proxy_reply->code = PW_CODE_ACCESS_CHALLENGE;
return 1;
case RLM_MODULE_OK:
return eaptls_success(handler, 0);
default:
- RDEBUG("Reply was unknown.");
+ RDEBUG("Reply was unknown");
break;
}
return 0;
}
-
-/*
- * Free a request.
- */
-static void my_request_free(void *data)
-{
- REQUEST *request = (REQUEST *)data;
-
- request_free(&request);
-}
-
+#endif /* WITH_PROXY */
/*
* Process the "diameter" contents of the tunneled data.
*/
-int eapttls_process(EAP_HANDLER *handler, tls_session_t *tls_session)
+int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
{
- int err;
- int rcode = PW_AUTHENTICATION_REJECT;
+ int code = PW_CODE_ACCESS_REJECT;
+ rlm_rcode_t rcode;
REQUEST *fake;
VALUE_PAIR *vp;
ttls_tunnel_t *t;
- const uint8_t *data;
+ uint8_t const *data;
size_t data_len;
REQUEST *request = handler->request;
*/
if (data_len == 0) {
if (t->authenticated) {
- RDEBUG("Got ACK, and the user was already authenticated.");
- return PW_AUTHENTICATION_ACK;
+ RDEBUG("Got ACK, and the user was already authenticated");
+ return PW_CODE_ACCESS_ACCEPT;
} /* else no session, no data, die. */
/*
* wrong.
*/
RDEBUG2("SSL_read Error");
- return PW_AUTHENTICATION_REJECT;
+ return PW_CODE_ACCESS_REJECT;
}
#ifndef NDEBUG
size_t i;
for (i = 0; i < data_len; i++) {
- if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TTLS tunnel data in %04x: ", i);
+ if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TTLS tunnel data in %04x: ", (int) i);
fprintf(fr_log_fp, "%02x ", data[i]);
#endif
if (!diameter_verify(request, data, data_len)) {
- return PW_AUTHENTICATION_REJECT;
+ return PW_CODE_ACCESS_REJECT;
}
/*
*/
fake = request_alloc_fake(request);
- rad_assert(fake->packet->vps == NULL);
+ rad_assert(!fake->packet->vps);
/*
* Add the tunneled attributes to the fake request.
*/
- fake->packet->vps = diameter2vp(request, tls_session->ssl, data, data_len);
+ fake->packet->vps = diameter2vp(request, fake, tls_session->ssl, data, data_len);
if (!fake->packet->vps) {
- request_free(&fake);
- return PW_AUTHENTICATION_REJECT;
+ talloc_free(fake);
+ return PW_CODE_ACCESS_REJECT;
}
/*
* Tell the request that it's a fake one.
*/
- vp = pairmake("Freeradius-Proxied-T<o", "127.0.0.1", T_OP_EQ);
- if (vp) {
- pairadd(&fake->packet->vps, vp);
- }
+ pairmake_packet("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
if ((debug_flag > 0) && fr_log_fp) {
RDEBUG("Got tunneled request");
/*
* Update other items in the REQUEST data structure.
*/
- fake->username = pairfind(fake->packet->vps, PW_USER_NAME);
- fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD);
+ fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ fake->password = pairfind(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 = pairfind(fake->packet->vps, PW_EAP_MESSAGE);
+ vp = pairfind(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
if (vp &&
(vp->length >= EAP_HEADER_LEN + 2) &&
(vp->vp_strvalue[0] == PW_EAP_RESPONSE) &&
(vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) &&
(vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) {
+ char *p;
+
/*
* Create & remember a User-Name
*/
- t->username = pairmake("User-Name", "", T_OP_EQ);
+ t->username = pairmake(t, NULL, "User-Name", NULL, T_OP_EQ);
rad_assert(t->username != NULL);
-
- memcpy(t->username->vp_strvalue, vp->vp_strvalue + 5,
- vp->length - 5);
t->username->length = vp->length - 5;
- t->username->vp_strvalue[t->username->length] = 0;
+
+ t->username->vp_strvalue = p = talloc_array(t->username, char,
+ t->username->length + 1);
+ memcpy(p, vp->vp_octets + 5, t->username->length);
+ p[t->username->length] = 0;
RDEBUG("Got tunneled identity of %s",
t->username->vp_strvalue);
* If there's a default EAP type,
* set it here.
*/
- if (t->default_eap_type != 0) {
- RDEBUG("Setting default EAP type for tunneled EAP session.");
- vp = paircreate(PW_EAP_TYPE,
- PW_TYPE_INTEGER);
+ if (t->default_method != 0) {
+ RDEBUG("Setting default EAP type for tunneled EAP session");
+ vp = paircreate(fake, PW_EAP_TYPE, 0);
rad_assert(vp != NULL);
- vp->vp_integer = t->default_eap_type;
+ vp->vp_integer = t->default_method;
pairadd(&fake->config_items, vp);
}
* as it's permitted to do EAP without
* user-name.
*/
- RDEBUG2("WARNING! No EAP-Identity found to start EAP conversation.");
+ RWDEBUG2("No EAP-Identity found to start EAP conversation");
}
} /* else there WAS a t->username */
if (t->username) {
- vp = paircopy(t->username);
+ vp = paircopy(fake->packet, t->username);
pairadd(&fake->packet->vps, vp);
- fake->username = pairfind(fake->packet->vps, PW_USER_NAME);
+ fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
}
} /* else the request ALREADY had a User-Name */
* Add the State attribute, too, if it exists.
*/
if (t->state) {
- vp = paircopy(t->state);
+ vp = paircopy(fake->packet, t->state);
if (vp) pairadd(&fake->packet->vps, vp);
}
*/
if (t->copy_request_to_tunnel) {
VALUE_PAIR *copy;
+ vp_cursor_t cursor;
- for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
+ 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->attribute > 255) &&
- (((vp->attribute >> 16) & 0xffff) == 0)) {
+ if ((vp->da->attr > 255) &&
+ (vp->da->vendor == 0)) {
continue;
}
* AND attributes which are copied there
* from below.
*/
- if (pairfind(fake->packet->vps, vp->attribute)) {
+ if (pairfind(fake->packet->vps, vp->da->attr, vp->da->vendor, TAG_ANY)) {
continue;
}
/*
* Some attributes are handled specially.
*/
- switch (vp->attribute) {
+ switch (vp->da->attr) {
/*
* NEVER copy Message-Authenticator,
* EAP-Message, or State. They're
* Don't copy from the head, we've already
* checked it.
*/
- copy = paircopy2(vp, vp->attribute);
+ copy = paircopy2(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
pairadd(&fake->packet->vps, copy);
}
}
- if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER)) != NULL) {
+ if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
fake->server = vp->vp_strvalue;
} else if (t->virtual_server) {
if ((debug_flag > 0) && fr_log_fp) {
RDEBUG("Sending tunneled request");
-
- debug_pair_list(fake->packet->vps);
-
- fprintf(fr_log_fp, "server %s {\n", fake->server);
}
/*
* Call authentication recursively, which will
* do PAP, CHAP, MS-CHAP, etc.
*/
- rad_authenticate(fake);
-
- /*
- * Note that we don't do *anything* with the reply
- * attributes.
- */
- if ((debug_flag > 0) && fr_log_fp) {
- fprintf(fr_log_fp, "} # server %s\n", fake->server);
-
- RDEBUG("Got tunneled reply code %d", fake->reply->code);
-
- debug_pair_list(fake->reply->vps);
- }
+ rad_virtual_server(fake);
/*
* Decide what to do with the reply.
*/
switch (fake->reply->code) {
case 0: /* No reply code, must be proxied... */
- vp = pairfind(fake->config_items, PW_PROXY_TO_REALM);
+#ifdef WITH_PROXY
+ vp = pairfind(fake->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY);
if (vp) {
eap_tunnel_data_t *tunnel;
RDEBUG("Tunneled authentication will be proxied to %s", vp->vp_strvalue);
* Tell the original request that it's going
* to be proxied.
*/
- pairmove2(&(request->config_items),
- &(fake->config_items),
- PW_PROXY_TO_REALM);
+ pairfilter(request, &request->config_items,
+ &fake->config_items,
+ PW_PROXY_TO_REALM, 0, TAG_ANY);
/*
* Seed the proxy packet with the
* tunneled request.
*/
- rad_assert(request->proxy == NULL);
- request->proxy = fake->packet;
+ rad_assert(!request->proxy);
+ request->proxy = talloc_steal(request, fake->packet);
+ memset(&request->proxy->src_ipaddr, 0,
+ sizeof(request->proxy->src_ipaddr));
+ memset(&request->proxy->src_ipaddr, 0,
+ sizeof(request->proxy->src_ipaddr));
+ request->proxy->src_port = 0;
+ request->proxy->dst_port = 0;
fake->packet = NULL;
rad_free(&fake->reply);
fake->reply = NULL;
/*
* Set up the callbacks for the tunnel
*/
- tunnel = rad_malloc(sizeof(*tunnel));
- memset(tunnel, 0, sizeof(*tunnel));
-
+ tunnel = talloc_zero(request, eap_tunnel_data_t);
tunnel->tls_session = tls_session;
tunnel->callback = eapttls_postproxy;
/*
* Associate the callback with the request.
*/
- rcode = request_data_add(request,
- request->proxy,
- REQUEST_DATA_EAP_TUNNEL_CALLBACK,
- tunnel, free);
- rad_assert(rcode == 0);
+ code = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK,
+ tunnel, false);
+ rad_assert(code == 0);
/*
* rlm_eap.c has taken care of associating
* So we associate the fake request with
* this request.
*/
- rcode = request_data_add(request,
- request->proxy,
- REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK,
- fake, my_request_free);
- rad_assert(rcode == 0);
+ code = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK,
+ fake, true);
+ rad_assert(code == 0);
fake = NULL;
/*
* Didn't authenticate the packet, but
* we're proxying it.
*/
- rcode = PW_STATUS_CLIENT;
+ code = PW_CODE_STATUS_CLIENT;
- } else {
+ } else
+#endif /* WITH_PROXY */
+ {
RDEBUG("No tunneled reply was found for request %d , and the request was not proxied: rejecting the user.",
request->number);
- rcode = PW_AUTHENTICATION_REJECT;
+ code = PW_CODE_ACCESS_REJECT;
}
break;
default:
/*
- * Returns RLM_MODULE_FOO, and we want to return
- * PW_FOO
+ * Returns RLM_MODULE_FOO, and we want to return PW_FOO
*/
- rcode = process_reply(handler, tls_session, request,
- fake->reply);
+ rcode = process_reply(handler, tls_session, request, fake->reply);
switch (rcode) {
case RLM_MODULE_REJECT:
- rcode = PW_AUTHENTICATION_REJECT;
+ code = PW_CODE_ACCESS_REJECT;
break;
case RLM_MODULE_HANDLED:
- rcode = PW_ACCESS_CHALLENGE;
+ code = PW_CODE_ACCESS_CHALLENGE;
break;
case RLM_MODULE_OK:
- rcode = PW_AUTHENTICATION_ACK;
+ code = PW_CODE_ACCESS_ACCEPT;
break;
default:
- rcode = PW_AUTHENTICATION_REJECT;
+ code = PW_CODE_ACCESS_REJECT;
break;
}
break;
}
- request_free(&fake);
+ talloc_free(fake);
- return rcode;
+ return code;
}