*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Copyright 2003 Alan DeKok <aland@freeradius.org>
+ * Copyright 2006 The FreeRADIUS server project
*/
-#include "eap_tls.h"
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
#include "eap_ttls.h"
/*
- * 0 1 2 3
- * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | AVP Code |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |V M r r r r r r| AVP Length |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Vendor-ID (opt) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Data ...
- * +-+-+-+-+-+-+-+-+
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | AVP Code |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V M r r r r r r| AVP Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Vendor-ID (opt) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+-+-+-+-+
*/
/*
* Verify that the diameter packet is valid.
*/
-static int diameter_verify(const uint8_t *data, int data_len)
+static int diameter_verify(const uint8_t *data, unsigned int data_len)
{
uint32_t attr;
uint32_t length;
unsigned int offset;
+ unsigned int data_left = data_len;
+
+ while (data_left > 0) {
+ if (data_len < 12) {
+ DEBUG2(" rlm_eap_ttls: Diameter attribute is too small to contain a Diameter header");
+ return 0;
+ }
- while (data_len > 0) {
+ rad_assert(data_left <= data_len);
memcpy(&attr, data, sizeof(attr));
data += 4;
attr = ntohl(attr);
attribute = (vendor << 16) | attr;
da = dict_attrbyvalue(attribute);
-
+
/*
* SHOULD check ((length & (1 << 30)) != 0)
* for the mandatory bit.
/*
* Too short or too long is bad.
*
- * FIXME: EAP-Message
+ * FIXME: EAP-Message
*/
- if ((length < offset) ||
- (length > (MAX_STRING_LEN + 8)) ||
- (length > data_len)) {
- DEBUG2(" rlm_eap_ttls: Tunneled attribute %d has invalid length %d", attr, length);
+ if (length < offset) {
+ DEBUG2(" rlm_eap_ttls: Tunneled attribute %d is too short (%d)to contain anything useful.", attr, length);
+ return 0;
+ }
+
+ if (length > (MAX_STRING_LEN + 8)) {
+ DEBUG2(" rlm_eap_ttls: Tunneled attribute %d is too long (%d) to pack into a RADIUS attribute.", attr, length);
+ return 0;
+ }
+
+ if (length > data_left) {
+ DEBUG2(" rlm_eap_ttls: Tunneled attribute %d is longer than room left in the packet (%d > %d).", attr, length, data_left);
return 0;
}
* Check for broken implementations, which don't
* pad the AVP to a 4-octet boundary.
*/
- if (data_len == length) break;
+ if (data_left == length) break;
/*
* The length does NOT include the padding, so
* Otherwise, if the attribute over-flows the end
* of the packet, die.
*/
- if (data_len > length) {
- data_len -= length;
- data += length - offset;
-
- } else if (data_len < length) {
+ if (data_left < length) {
DEBUG2(" rlm_eap_ttls: ERROR! Diameter attribute overflows packet!");
return 0;
-
- } else { /* equal, end of packet... */
- break;
}
+
+ /*
+ * 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.
+ */
+ data_left -= length;
+ data += length - offset;
}
/*
{
uint32_t attr;
uint32_t length;
- int offset;
+ unsigned int offset;
int size;
+ unsigned int data_left = data_len;
VALUE_PAIR *first = NULL;
VALUE_PAIR **last = &first;
VALUE_PAIR *vp;
- while (data_len > 0) {
+ while (data_left > 0) {
+ rad_assert(data_left <= data_len);
memcpy(&attr, data, sizeof(attr));
data += 4;
attr = ntohl(attr);
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->type) {
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
- if (size != vp->length) {
- DEBUG2(" rlm_eap_ttls: Invalid length attribute %d",
- attr);
- pairfree(&first);
- return NULL;
- }
- memcpy(&vp->lvalue, data, vp->length);
-
- /*
- * Stored in host byte order: change it.
- */
- vp->lvalue = ntohl(vp->lvalue);
- break;
-
+ if (size != vp->length) {
+ DEBUG2(" rlm_eap_ttls: Invalid length attribute %d",
+ attr);
+ pairfree(&first);
+ return NULL;
+ }
+ memcpy(&vp->lvalue, data, vp->length);
+
+ /*
+ * Stored in host byte order: change it.
+ */
+ vp->lvalue = ntohl(vp->lvalue);
+ break;
+
case PW_TYPE_IPADDR:
- if (size != vp->length) {
- DEBUG2(" rlm_eap_ttls: Invalid length attribute %d",
- attr);
- pairfree(&first);
- return NULL;
- }
+ if (size != vp->length) {
+ DEBUG2(" rlm_eap_ttls: Invalid length attribute %d",
+ attr);
+ pairfree(&first);
+ return NULL;
+ }
memcpy(&vp->lvalue, data, vp->length);
/*
/*
* String, octet, etc. Copy the data from the
* value field over verbatim.
+ *
+ * FIXME: Ipv6 attributes ?
+ *
*/
default:
- vp->length = size;
- memcpy(vp->strvalue, data, vp->length);
- break;
+ vp->length = size;
+ memcpy(vp->vp_strvalue, data, vp->length);
+ break;
}
/*
*/
switch (vp->attribute) {
case PW_USER_PASSWORD:
- {
- int i;
-
- for (i = 0; i < vp->length; i++) {
- if (vp->strvalue[i] == 0) {
- vp->length = i;
- break;
- }
- }
- }
+ rad_assert(vp->length <= 128); /* RFC requirements */
+
+ /*
+ * 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);
break;
/*
sizeof(challenge));
for (i = 0; i < vp->length; i++) {
- if (challenge[i] != vp->strvalue[i]) {
+ if (challenge[i] != vp->vp_strvalue[i]) {
DEBUG2(" TTLS: Tunneled challenge is incorrect");
pairfree(&first);
return NULL;
last = &(vp->next);
/*
+ * 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;
- data_len -= length;
+ rad_assert(data_left >= length);
+ data_left -= length;
data += length - offset; /* already updated */
}
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
default:
- memcpy(p, vp->strvalue, vp->length);
+ memcpy(p, vp->vp_strvalue, vp->length);
length = vp->length;
break;
}
* Align the data to a multiple of 4 bytes.
*/
if ((total & 0x03) != 0) {
- int i;
+ unsigned int i;
- length = total & 0x03;
+ length = 4 - (total & 0x03);
for (i = 0; i < length; i++) {
*p = '\0';
p++;
/*
* Write the data in the buffer to the SSL session.
*/
-
if (total > 0) {
#ifndef NDEBUG
- int i;
-
+ unsigned int i;
+
if (debug_flag > 2) {
for (i = 0; i < total; i++) {
if ((i & 0x0f) == 0) printf(" TTLS tunnel data out %04x: ", i);
-
+
printf("%02x ", buffer[i]);
-
+
if ((i & 0x0f) == 0x0f) printf("\n");
}
if ((total & 0x0f) != 0) printf("\n");
}
#endif
- record_plus(&tls_session->clean_in, buffer, total);
+ (tls_session->record_plus)(&tls_session->clean_in, buffer, total);
/*
* FIXME: Check the return code.
return 1;
}
+/*
+ * 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)
+{
+ int rcode = RLM_MODULE_REJECT;
+ VALUE_PAIR *vp;
+ ttls_tunnel_t *t = tls_session->opaque;
+
+ handler = handler; /* -Wunused */
+
+ /*
+ * If the response packet was Access-Accept, then
+ * we're OK. If not, die horribly.
+ *
+ * FIXME: Take MS-CHAP2-Success attribute, and
+ * tunnel it back to the client, to authenticate
+ * ourselves to the client.
+ *
+ * FIXME: If we have an Access-Challenge, then
+ * the Reply-Message is tunneled back to the client.
+ *
+ * FIXME: If we have an EAP-Message, then that message
+ * must be tunneled back to the client.
+ *
+ * FIXME: If we have an Access-Challenge with a State
+ * attribute, then do we tunnel that to the client, or
+ * keep track of it ourselves?
+ *
+ * FIXME: EAP-Messages can only start with 'identity',
+ * NOT 'eap start', so we should check for that....
+ */
+ switch (reply->code) {
+ case PW_AUTHENTICATION_ACK:
+ DEBUG2(" TTLS: Got tunneled Access-Accept");
+
+ rcode = RLM_MODULE_OK;
+
+ /*
+ * MS-CHAP2-Success means that we do NOT return
+ * an Access-Accept, but instead tunnel that
+ * attribute to the client, and keep going with
+ * the TTLS session. Once the client accepts
+ * our identity, it will respond with an empty
+ * packet, and we will send EAP-Success.
+ */
+ vp = NULL;
+ pairmove2(&vp, &reply->vps, PW_MSCHAP2_SUCCESS);
+ if (vp) {
+ DEBUG2(" TTLS: Got MS-CHAP2-Success, tunneling it to the client in a challenge.");
+ rcode = RLM_MODULE_HANDLED;
+ 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));
+
+ /*
+ * Use the tunneled reply, but not now.
+ */
+ if (t->use_tunneled_reply) {
+ t->reply = reply->vps;
+ reply->vps = NULL;
+ }
+
+ } else { /* no MS-CHAP2-Success */
+ /*
+ * Can only have EAP-Message if there's
+ * no MS-CHAP2-Success. (FIXME: EAP-MSCHAP?)
+ *
+ * We also do NOT tunnel the EAP-Success
+ * attribute back to the client, as the client
+ * can figure it out, from the non-tunneled
+ * EAP-Success packet.
+ */
+ pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE);
+ pairfree(&vp);
+ }
+
+ /*
+ * Handle the ACK, by tunneling any necessary reply
+ * VP's back to the client.
+ */
+ if (vp) {
+ vp2diameter(tls_session, vp);
+ pairfree(&vp);
+ }
+
+ /*
+ * If we've been told to use the attributes from
+ * the reply, then do so.
+ *
+ * WARNING: This may leak information about the
+ * tunneled user!
+ */
+ if (t->use_tunneled_reply) {
+ pairdelete(&reply->vps, PW_PROXY_STATE);
+ pairadd(&request->reply->vps, reply->vps);
+ reply->vps = NULL;
+ }
+ break;
+
+
+ case PW_AUTHENTICATION_REJECT:
+ DEBUG2(" TTLS: Got tunneled Access-Reject");
+ rcode = RLM_MODULE_REJECT;
+ break;
+
+ /*
+ * Handle Access-Challenge, but only if we
+ * send tunneled reply data. This is because
+ * an Access-Challenge means that we MUST tunnel
+ * a Reply-Message to the client.
+ */
+ case PW_ACCESS_CHALLENGE:
+ DEBUG2(" TTLS: Got tunneled Access-Challenge");
+
+ /*
+ * Keep the State attribute, if necessary.
+ *
+ * Get rid of the old State, too.
+ */
+ pairfree(&t->state);
+ pairmove2(&t->state, &reply->vps, PW_STATE);
+
+ /*
+ * We should really be a bit smarter about this,
+ * and move over only those attributes which
+ * are relevant to the authentication request,
+ * but that's a lot more work, and this "dumb"
+ * method works in 99.9% of the situations.
+ */
+ vp = NULL;
+ pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE);
+
+ /*
+ * There MUST be a Reply-Message in the challenge,
+ * which we tunnel back to the client.
+ *
+ * If there isn't one in the reply VP's, then
+ * we MUST create one, with an empty string as
+ * it's value.
+ */
+ pairmove2(&vp, &reply->vps, PW_REPLY_MESSAGE);
+
+ /*
+ * Handle the ACK, by tunneling any necessary reply
+ * VP's back to the client.
+ */
+ if (vp) {
+ vp2diameter(tls_session, vp);
+ pairfree(&vp);
+ }
+ rcode = RLM_MODULE_HANDLED;
+ break;
+
+ default:
+ DEBUG2(" TTLS: Unknown RADIUS packet type %d: rejecting tunneled user", reply->code);
+ rcode = RLM_MODULE_INVALID;
+ break;
+ }
+
+ return rcode;
+}
+
+
+/*
+ * Do post-proxy processing,
+ */
+static int eapttls_postproxy(EAP_HANDLER *handler, void *data)
+{
+ int rcode;
+ tls_session_t *tls_session = (tls_session_t *) data;
+ REQUEST *fake;
+
+ DEBUG2(" TTLS: Passing reply from proxy back into the tunnel.");
+
+ /*
+ * If there was a fake request associated with the proxied
+ * request, do more processing of it.
+ */
+ fake = (REQUEST *) request_data_get(handler->request,
+ handler->request->proxy,
+ REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK);
+
+ /*
+ * Do the callback, if it exists, and if it was a success.
+ */
+ if (fake && (handler->request->proxy_reply->code == PW_AUTHENTICATION_ACK)) {
+ VALUE_PAIR *vp;
+ REQUEST *request = handler->request;
+
+ /*
+ * Terrible hacks.
+ */
+ rad_assert(fake->packet == NULL);
+ fake->packet = request->proxy;
+ request->proxy = NULL;
+
+ rad_assert(fake->reply == NULL);
+ fake->reply = request->proxy_reply;
+ request->proxy_reply = NULL;
+
+ /*
+ * Perform a post-auth stage for the tunneled
+ * session.
+ */
+ fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+ rcode = rad_postauth(fake);
+ DEBUG2(" POST-AUTH %d", rcode);
+
+#ifndef NDEBUG
+ if (debug_flag > 0) {
+ printf(" TTLS: Final reply from tunneled session code %d\n",
+ fake->reply->code);
+
+ for (vp = fake->reply->vps; vp != NULL; vp = vp->next) {
+ putchar('\t');vp_print(stdout, vp);putchar('\n');
+ }
+ }
+#endif
+
+ /*
+ * Terrible hacks.
+ */
+ request->proxy = fake->packet;
+ fake->packet = NULL;
+ request->proxy_reply = fake->reply;
+ fake->reply = NULL;
+
+ /*
+ * And we're done with this request.
+ */
+
+ switch (rcode) {
+ case RLM_MODULE_FAIL:
+ request_free(&fake);
+ eaptls_fail(handler->eap_ds, 0);
+ return 0;
+ break;
+
+ default: /* Don't Do Anything */
+ DEBUG2(" TTLS: Got reply %d",
+ request->proxy_reply->code);
+ break;
+ }
+ }
+ request_free(&fake); /* robust if fake == NULL */
+
+ /*
+ * Process the reply from the home server.
+ */
+ rcode = process_reply(handler, tls_session, handler->request,
+ handler->request->proxy_reply);
+
+ /*
+ * The proxy code uses the reply from the home server as
+ * the basis for the reply to the NAS. We don't want that,
+ * so we toss it, after we've had our way with it.
+ */
+ pairfree(&handler->request->proxy_reply->vps);
+
+ switch (rcode) {
+ case RLM_MODULE_REJECT:
+ DEBUG2(" TTLS: Reply was rejected");
+ break;
+
+ case RLM_MODULE_HANDLED:
+ DEBUG2(" TTLS: Reply was handled");
+ eaptls_request(handler->eap_ds, tls_session);
+ return 1;
+
+ case RLM_MODULE_OK:
+ DEBUG2(" TTLS: Reply was OK");
+ eaptls_success(handler->eap_ds, 0);
+ eaptls_gen_mppe_keys(&handler->request->reply->vps,
+ tls_session->ssl,
+ "ttls keying material");
+ return 1;
+
+ default:
+ DEBUG2(" TTLS: Reply was unknown.");
+ break;
+ }
+
+ eaptls_fail(handler->eap_ds, 0);
+ return 0;
+}
+
+
+/*
+ * Free a request.
+ */
+static void my_request_free(void *data)
+{
+ REQUEST *request = (REQUEST *)data;
+
+ request_free(&request);
+}
+
/*
* Process the "diameter" contents of the tunneled data.
*/
-int eapttls_process(REQUEST *request, tls_session_t *tls_session)
+int eapttls_process(EAP_HANDLER *handler, tls_session_t *tls_session)
{
- int i, err;
+ int err;
int rcode = PW_AUTHENTICATION_REJECT;
REQUEST *fake;
VALUE_PAIR *vp;
const uint8_t *data;
unsigned int data_len;
char buffer[1024];
+ REQUEST *request = handler->request;
/*
* Grab the dirty data, and copy it to our buffer.
*
* I *really* don't like these 'record_t' things...
*/
- data_len = record_minus(&tls_session->dirty_in, buffer, sizeof(buffer));
+ data_len = (tls_session->record_minus)(&tls_session->dirty_in, buffer, sizeof(buffer));
data = buffer;
/*
* go there, too...
*/
BIO_write(tls_session->into_ssl, buffer, data_len);
- record_init(&tls_session->clean_out);
+ (tls_session->record_init)(&tls_session->clean_out);
/*
* Read (and decrypt) the tunneled data from the SSL session,
/*
* If there's no data, maybe this is an ACK to an
* MS-CHAP2-Success.
- */
+ */
if (err == 0) {
if (t->authenticated) {
DEBUG2(" TTLS: Got ACK, and the user was already authenticated.");
#ifndef NDEBUG
if (debug_flag > 2) {
+ unsigned int i;
+
for (i = 0; i < data_len; i++) {
if ((i & 0x0f) == 0) printf(" TTLS tunnel data in %04x: ", i);
-
+
printf("%02x ", data[i]);
-
+
if ((i & 0x0f) == 0x0f) printf("\n");
}
if ((data_len & 0x0f) != 0) printf("\n");
}
#endif
- if (!diameter_verify(data, (int) data_len)) {
+ if (!diameter_verify(data, data_len)) {
return PW_AUTHENTICATION_REJECT;
}
#ifndef NDEBUG
if (debug_flag > 0) {
- printf(" TTLS: Got tunneled request\n");
-
- for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
- putchar('\t');vp_print(stdout, vp);putchar('\n');
- }
+ printf(" TTLS: Got tunneled request\n");
+
+ for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
+ putchar('\t');vp_print(stdout, vp);putchar('\n');
+ }
}
#endif
* Update other items in the REQUEST data structure.
*/
fake->username = pairfind(fake->packet->vps, PW_USER_NAME);
- fake->password = pairfind(fake->packet->vps, PW_PASSWORD);
+ fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD);
/*
* No User-Name, try to create one from stored data.
vp = pairfind(fake->packet->vps, PW_EAP_MESSAGE);
if (vp &&
(vp->length >= EAP_HEADER_LEN + 2) &&
- (vp->strvalue[0] == PW_EAP_RESPONSE) &&
- (vp->strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) &&
- (vp->strvalue[EAP_HEADER_LEN + 1] != 0)) {
+ (vp->vp_strvalue[0] == PW_EAP_RESPONSE) &&
+ (vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) &&
+ (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) {
/*
* Create & remember a User-Name
*/
t->username = pairmake("User-Name", "", T_OP_EQ);
rad_assert(t->username != NULL);
-
- memcpy(t->username->strvalue, vp->strvalue + 5,
+
+ memcpy(t->username->vp_strvalue, vp->vp_strvalue + 5,
vp->length - 5);
t->username->length = vp->length - 5;
- t->username->strvalue[t->username->length] = 0;
-
+ t->username->vp_strvalue[t->username->length] = 0;
+
DEBUG2(" TTLS: Got tunneled identity of %s",
- t->username->strvalue);
+ t->username->vp_strvalue);
/*
* If there's a default EAP type,
*/
if (t->state) {
DEBUG2(" TTLS: Adding old state with %02x %02x",
- t->state->strvalue[0], t->state->strvalue[1]);
+ t->state->vp_strvalue[0], t->state->vp_strvalue[1]);
vp = paircopy(t->state);
if (vp) pairadd(&fake->packet->vps, vp);
}
}
}
#endif
+
/*
- * If the response packet was Access-Accept, then
- * we're OK. If not, die horribly.
- *
- * FIXME: Take MS-CHAP2-Success attribute, and
- * tunnel it back to the client, to authenticate
- * ourselves to the client.
- *
- * FIXME: If we have an Access-Challenge, then
- * the Reply-Message is tunneled back to the client.
- *
- * FIXME: If we have an EAP-Message, then that message
- * must be tunneled back to the client.
- *
- * FIXME: If we have an Access-Challenge with a State
- * attribute, then do we tunnel that to the client, or
- * keep track of it ourselves?
- *
- * FIXME: EAP-Messages can only start with 'identity',
- * NOT 'eap start', so we should check for that....
+ * Decide what to do with the reply.
*/
- rcode = 0;
- if (fake->reply->code == PW_AUTHENTICATION_ACK) {
- DEBUG2(" TTLS: Got tunneled Access-Accept");
-
- rcode = fake->reply->code;
-
- /*
- * MS-CHAP2-Success means that we do NOT return
- * an Access-Accept, but instead tunnel that
- * attribute to the client, and keep going with
- * the TTLS session. Once the client accepts
- * our identity, it will respond with an empty
- * packet, and we will send EAP-Success.
- */
- vp = NULL;
- pairmove2(&vp, &fake->reply->vps, PW_MSCHAP2_SUCCESS);
+ switch (fake->reply->code) {
+ case 0: /* No reply code, must be proxied... */
+ vp = pairfind(fake->config_items, PW_PROXY_TO_REALM);
if (vp) {
-#if 1
+ eap_tunnel_data_t *tunnel;
+ DEBUG2(" TTLS: Tunneled authentication will be proxied to %s", vp->vp_strvalue);
+
/*
- * FIXME: Tunneling MS-CHAP2-Success causes
- * the only client we have access to, to die.
- *
- * We don't want that...
+ * Tell the original request that it's going
+ * to be proxied.
*/
- pairfree(&vp);
-#else
- DEBUG2(" TTLS: Got MS-CHAP2-Success, tunneling it to the client in a challenge.");
- rcode = PW_ACCESS_CHALLENGE;
- t->authenticated = TRUE;
-#endif
- } else { /* no MS-CHAP2-Success */
+ pairmove2(&(request->config_items),
+ &(fake->config_items),
+ PW_PROXY_TO_REALM);
+
/*
- * Can only have EAP-Message if there's
- * no MS-CHAP2-Success. (FIXME: EAP-MSCHAP?)
- *
- * We also do NOT tunnel the EAP-Success
- * attribute back to the client, as the client
- * can figure it out, from the non-tunneled
- * EAP-Success packet.
+ * Seed the proxy packet with the
+ * tunneled request.
*/
- pairmove2(&vp, &fake->reply->vps, PW_EAP_MESSAGE);
- pairfree(&vp);
+ rad_assert(request->proxy == NULL);
+ request->proxy = fake->packet;
+ fake->packet = NULL;
+ rad_free(&fake->reply);
+ fake->reply = NULL;
/*
- * If we've been told to use the attributes from
- * the reply, then do so.
- *
- * WARNING: This may leak information about the
- * tunneled user!
+ * Set up the callbacks for the tunnel
*/
- if (t->use_tunneled_reply) {
- pairadd(&request->reply->vps, fake->reply->vps);
- fake->reply->vps = NULL;
- }
- }
+ tunnel = rad_malloc(sizeof(*tunnel));
+ memset(tunnel, 0, sizeof(*tunnel));
- /*
- * Handle the ACK, by tunneling any necessary reply
- * VP's back to the client.
- */
- if (vp) {
- vp2diameter(tls_session, vp);
- pairfree(&vp);
- }
-
- /*
- * Handle Access-Challenge, but only if we
- * send tunneled reply data. This is because
- * an Access-Challenge means that we MUST tunnel
- * a Reply-Message to the client.
- */
- } else if (fake->reply->code == PW_ACCESS_CHALLENGE) {
- DEBUG2(" TTLS: Got tunneled Access-Challenge");
+ tunnel->tls_session = tls_session;
+ tunnel->callback = eapttls_postproxy;
- /*
- * Keep the State attribute, if necessary.
- *
- * Get rid of the old State, too.
- */
- pairfree(&t->state);
- pairmove2(&t->state, &fake->reply->vps, PW_STATE);
+ /*
+ * Associate the callback with the request.
+ */
+ rcode = request_data_add(request,
+ request->proxy,
+ REQUEST_DATA_EAP_TUNNEL_CALLBACK,
+ tunnel, free);
+ rad_assert(rcode == 0);
+
+ /*
+ * rlm_eap.c has taken care of associating
+ * the handler with the fake request.
+ *
+ * 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);
+ fake = NULL;
- /*
- * We should really be a bit smarter about this,
- * and move over only those attributes which
- * are relevant to the authentication request,
- * but that's a lot more work, and this "dumb"
- * method works in 99.9% of the situations.
- */
- vp = NULL;
- pairmove2(&vp, &fake->reply->vps, PW_EAP_MESSAGE);
+ /*
+ * Didn't authenticate the packet, but
+ * we're proxying it.
+ */
+ rcode = PW_STATUS_CLIENT;
- /*
- * There MUST be a Reply-Message in the challenge,
- * which we tunnel back to the client.
- *
- * If there isn't one in the reply VP's, then
- * we MUST create one, with an empty string as
- * it's value.
- */
- pairmove2(&vp, &fake->reply->vps, PW_REPLY_MESSAGE);
+ } else {
+ DEBUG2(" TTLS: No tunneled reply was found for request %d , and the request was not proxied: rejecting the user.",
+ request->number);
+ rcode = PW_AUTHENTICATION_REJECT;
+ }
+ break;
+ default:
/*
- * Handle the ACK, by tunneling any necessary reply
- * VP's back to the client.
+ * Returns RLM_MODULE_FOO, and we want to return
+ * PW_FOO
*/
- if (vp) {
- vp2diameter(tls_session, vp);
- pairfree(&vp);
+ rcode = process_reply(handler, tls_session, request,
+ fake->reply);
+ switch (rcode) {
+ case RLM_MODULE_REJECT:
+ rcode = PW_AUTHENTICATION_REJECT;
+ break;
+
+ case RLM_MODULE_HANDLED:
+ rcode = PW_ACCESS_CHALLENGE;
+ break;
+
+ case RLM_MODULE_OK:
+ rcode = PW_AUTHENTICATION_ACK;
+ break;
+
+ default:
+ rcode = PW_AUTHENTICATION_REJECT;
+ break;
}
- rcode = fake->reply->code;
-
- } else {
- DEBUG2(" TTLS: Rejecting tunneled user");
+ break;
}
-
+
request_free(&fake);
return rcode;
}
-