2 * peap.c contains the interfaces that are called from eap
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2003 Alan DeKok <aland@freeradius.org>
26 * Send protected EAP-Failure
28 * Result-TLV = Failure
30 static int eappeap_failure(EAP_HANDLER *handler, tls_session_t *tls_session)
32 uint8_t tlv_packet[11];
34 DEBUG2(" rlm_eap_peap: FAILURE");
36 tlv_packet[0] = PW_EAP_REQUEST;
37 tlv_packet[1] = handler->eap_ds->response->id +1;
39 tlv_packet[3] = 11; /* length of this packet */
40 tlv_packet[4] = PW_EAP_TLV;
42 tlv_packet[6] = EAP_TLV_ACK_RESULT;
44 tlv_packet[8] = 2; /* length of the data portion */
46 tlv_packet[10] = EAP_TLV_FAILURE;
48 record_plus(&tls_session->clean_in, tlv_packet, 11);
51 * FIXME: Check the return code.
53 tls_handshake_send(tls_session);
54 record_init(&tls_session->clean_in);
61 * Send protected EAP-Success
63 * Result-TLV = Success
65 static int eappeap_success(EAP_HANDLER *handler, tls_session_t *tls_session)
67 uint8_t tlv_packet[11];
69 DEBUG2(" rlm_eap_peap: SUCCESS");
71 tlv_packet[0] = PW_EAP_REQUEST;
72 tlv_packet[1] = handler->eap_ds->response->id +1;
74 tlv_packet[3] = 11; /* length of this packet */
75 tlv_packet[4] = PW_EAP_TLV;
76 tlv_packet[5] = 0x80; /* mandatory AVP */
77 tlv_packet[6] = EAP_TLV_ACK_RESULT;
79 tlv_packet[8] = 2; /* length of the data portion */
81 tlv_packet[10] = EAP_TLV_SUCCESS;
83 record_plus(&tls_session->clean_in, tlv_packet, 11);
86 * FIXME: Check the return code.
88 tls_handshake_send(tls_session);
89 record_init(&tls_session->clean_in);
96 * Verify the tunneled EAP message.
98 static int eapmessage_verify(const uint8_t *data, unsigned int data_len)
100 const eap_packet_t *eap_packet = (const eap_packet_t *) data;
104 if (!data || (data_len <= 1)) {
110 case PW_EAP_IDENTITY:
111 memcpy(identity, data + 1, data_len - 1);
112 identity[data_len - 1] = '\0';
113 DEBUG2(" rlm_eap_peap: Identity - %s", identity);
118 * If the first byte of the packet is
119 * EAP-Response, and the EAP data is a TLV,
120 * then it looks OK...
122 case PW_EAP_RESPONSE:
123 if (eap_packet->data[0] == PW_EAP_TLV) {
124 DEBUG2(" rlm_eap_peap: Received EAP-TLV response.");
127 DEBUG2(" rlm_eap_peap: Got something weird.");
132 * We normally do Microsoft MS-CHAPv2 (26), versus
133 * Cisco MS-CHAPv2 (29).
135 case PW_EAP_MSCHAPV2:
137 DEBUG2(" rlm_eap_peap: EAP type %d", eap_type);
146 * Convert a pseudo-EAP packet to a list of VALUE_PAIR's.
148 static VALUE_PAIR *eap2vp(EAP_DS *eap_ds,
149 const uint8_t *data, unsigned int data_len)
151 VALUE_PAIR *vp = NULL;
154 * Sanity check this...
156 if (data_len + EAP_HEADER_LEN > MAX_STRING_LEN) {
157 radlog(L_ERR, "rlm_eap_peap: EAP Response packet is too large. Code must be fixed to handle this.");
161 vp = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
163 DEBUG2(" rlm_eap_peap: Failure in creating VP");
168 * Hand-build an EAP packet from the crap in PEAP version 0.
170 vp->strvalue[0] = PW_EAP_RESPONSE;
171 vp->strvalue[1] = eap_ds->response->id;
173 vp->strvalue[3] = EAP_HEADER_LEN + data_len;
175 memcpy(vp->strvalue + EAP_HEADER_LEN, data, data_len);
176 vp->length = EAP_HEADER_LEN + data_len;
183 * Convert a list of VALUE_PAIR's to an EAP packet, through the
184 * simple expedient of dumping the EAP message
186 static int vp2eap(tls_session_t *tls_session, VALUE_PAIR *vp)
188 if (vp->next != NULL) {
189 radlog(L_ERR, "rlm_eap_peap: EAP Request packet is too large. Code must be fixed to handle this.");
194 * Skip the id, code, and length. Just write the EAP
195 * type & data to the client.
198 if (debug_flag > 2) {
200 int total = vp->length - 4;
202 if (debug_flag > 0) for (i = 0; i < total; i++) {
203 if ((i & 0x0f) == 0) printf(" PEAP tunnel data out %04x: ", i);
205 printf("%02x ", vp->strvalue[i + 4]);
207 if ((i & 0x0f) == 0x0f) printf("\n");
209 if ((total & 0x0f) != 0) printf("\n");
214 * Send the EAP data, WITHOUT the header.
217 record_plus(&tls_session->clean_in, vp->strvalue + EAP_HEADER_LEN,
218 vp->length - EAP_HEADER_LEN);
220 record_plus(&tls_session->clean_in, vp->strvalue, vp->length);
222 tls_handshake_send(tls_session);
223 record_init(&tls_session->clean_in);
230 * See if there's a TLV in the response.
232 static int eappeap_check_tlv(const uint8_t *data)
234 const eap_packet_t *eap_packet = (const eap_packet_t *) data;
237 * Look for success or failure.
239 if ((eap_packet->code == PW_EAP_RESPONSE) &&
240 (eap_packet->data[0] == PW_EAP_TLV)) {
241 if (data[10] == EAP_TLV_SUCCESS) {
245 if (data[10] == EAP_TLV_FAILURE) {
246 DEBUG2(" rlm_eap_peap: Client rejected our response. The password is probably incorrect.");
255 * Process the pseudo-EAP contents of the tunneled data.
257 int eappeap_process(EAP_HANDLER *handler, tls_session_t *tls_session)
260 peap_tunnel_t *t = tls_session->opaque;
263 int rcode = RLM_MODULE_REJECT;
265 unsigned int data_len;
266 unsigned char buffer[1024];
271 REQUEST *request = handler->request;
272 EAP_DS *eap_ds = handler->eap_ds;
275 * Grab the dirty data, and copy it to our buffer.
277 * I *really* don't like these 'record_t' things...
279 data_len = record_minus(&tls_session->dirty_in, buffer, sizeof(buffer));
283 * Write the data from the dirty buffer (i.e. packet
284 * data) into the buffer which we will give to SSL for
287 * Some of this code COULD technically go into the TLS
288 * module, in eaptls_process(), where it returns EAPTLS_OK.
290 * Similarly, the writing of data to the SSL context could
293 BIO_write(tls_session->into_ssl, buffer, data_len);
294 record_init(&tls_session->clean_out);
297 * Read (and decrypt) the tunneled data from the SSL session,
298 * and put it into the decrypted data buffer.
300 err = SSL_read(tls_session->ssl, tls_session->clean_out.data,
301 sizeof(tls_session->clean_out.data));
304 * FIXME: Call SSL_get_error() to see what went
307 radlog(L_INFO, "rlm_eap_peap: SSL_read Error");
308 return RLM_MODULE_REJECT;
312 * If there's no data, maybe this is an ACK to an
317 * FIXME: Call SSL_get_error() to see what went
320 radlog(L_INFO, "rlm_eap_peap: No data inside of the tunnel.");
321 return RLM_MODULE_REJECT;
324 data_len = tls_session->clean_out.used = err;
325 data = tls_session->clean_out.data;
328 if (debug_flag > 2) for (i = 0; i < data_len; i++) {
329 if ((i & 0x0f) == 0) printf(" PEAP tunnel data in %04x: ", i);
331 printf("%02x ", data[i]);
333 if ((i & 0x0f) == 0x0f) printf("\n");
335 if ((data_len & 0x0f) != 0) printf("\n");
338 if (!eapmessage_verify(data, data_len)) {
339 return RLM_MODULE_REJECT;
342 DEBUG2(" rlm_eap_peap: Tunneled data is valid.");
345 * If we authenticated the user, then it's OK.
347 if (t->status == PEAP_STATUS_SENT_TLV_SUCCESS) {
348 if (eappeap_check_tlv(data)) {
349 DEBUG2(" rlm_eap_peap: Success");
350 return RLM_MODULE_OK;
353 return RLM_MODULE_REJECT;
355 } else if (t->status == PEAP_STATUS_SENT_TLV_FAILURE) {
356 DEBUG2(" rlm_eap_peap: Had sent TLV failure, rejecting.");
357 return RLM_MODULE_REJECT;
360 fake = request_alloc_fake(request);
362 rad_assert(fake->packet->vps == NULL);
364 fake->packet->vps = eap2vp(eap_ds, data, data_len);
365 if (!fake->packet->vps) {
366 DEBUG2(" rlm_eap_peap: Unable to convert tunneled EAP packet to internal server data structures");
367 return PW_AUTHENTICATION_REJECT;
371 if (debug_flag > 0) {
372 printf(" PEAP: Got tunneled EAP-Message\n");
374 for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
375 putchar('\t');vp_print(stdout, vp);putchar('\n');
381 * Tell the request that it's a fake one.
383 vp = pairmake("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
385 pairadd(&fake->packet->vps, vp);
389 * Update other items in the REQUEST data structure.
392 if ((data[0] == PW_EAP_IDENTITY) && (data_len > 1)) {
393 t->username = pairmake("User-Name", "", T_OP_EQ);
394 rad_assert(t->username != NULL);
396 memcpy(t->username->strvalue, data+1, data_len - 1);
397 t->username->length = data_len -1;
398 t->username->strvalue[t->username->length] = 0;
399 DEBUG2(" PEAP: Got tunneled identity of %s", t->username->strvalue);
402 * If there's a default EAP type,
405 if (t->default_eap_type != 0) {
406 DEBUG2(" PEAP: Setting default EAP type for tunneled EAP session.");
407 vp = pairmake("EAP-Type", "0", T_OP_EQ);
408 vp->lvalue = t->default_eap_type;
409 pairadd(&fake->config_items, vp);
412 } /* else there WAS a t->username */
415 vp = paircopy(t->username);
416 pairadd(&fake->packet->vps, vp);
417 fake->username = pairfind(fake->packet->vps, PW_USER_NAME);
421 * Add the State attribute, too, if it exists.
424 DEBUG2(" PEAP: Adding old state with %02x %02x",
425 t->state->strvalue[0], t->state->strvalue[1]);
426 vp = paircopy(t->state);
427 if (vp) pairadd(&fake->packet->vps, vp);
431 * If this is set, we copy SOME of the request attributes
432 * from outside of the tunnel to inside of the tunnel.
434 * We copy ONLY those attributes which do NOT already
435 * exist in the tunneled request.
437 * This code is copied from ../rlm_eap_ttls/ttls.c
439 if (t->copy_request_to_tunnel) {
442 for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
444 * The attribute is a server-side thingy,
447 if ((vp->attribute > 255) &&
448 (((vp->attribute >> 16) & 0xffff) == 0)) {
453 * The outside attribute is already in the
454 * tunnel, don't copy it.
456 * This works for BOTH attributes which
457 * are originally in the tunneled request,
458 * AND attributes which are copied there
461 if (pairfind(fake->packet->vps, vp->attribute)) {
466 * Some attributes are handled specially.
468 switch (vp->attribute) {
470 * NEVER copy Message-Authenticator,
471 * EAP-Message, or State. They're
472 * only for outside of the tunnel.
475 case PW_USER_PASSWORD:
476 case PW_CHAP_PASSWORD:
477 case PW_CHAP_CHALLENGE:
479 case PW_MESSAGE_AUTHENTICATOR:
486 * By default, copy it over.
493 * Don't copy from the head, we've already
496 copy = paircopy2(vp, vp->attribute);
497 pairadd(&fake->packet->vps, copy);
502 if (debug_flag > 0) {
503 printf(" PEAP: Sending tunneled request\n");
505 for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
506 putchar('\t');vp_print(stdout, vp);putchar('\n');
512 * Call authentication recursively, which will
513 * do PAP, CHAP, MS-CHAP, etc.
515 rad_authenticate(fake);
518 * Note that we don't do *anything* with the reply
522 if (debug_flag > 0) {
523 printf(" PEAP: Got tunneled reply RADIUS code %d\n",
526 for (vp = fake->reply->vps; vp != NULL; vp = vp->next) {
527 putchar('\t');vp_print(stdout, vp);putchar('\n');
532 switch (fake->reply->code) {
533 case PW_AUTHENTICATION_ACK:
534 DEBUG2(" PEAP: Tunneled authentication was successful.");
535 t->status = PEAP_STATUS_SENT_TLV_SUCCESS;
536 eappeap_success(handler, tls_session);
537 rcode = RLM_MODULE_HANDLED;
540 * If we've been told to use the attributes from
541 * the reply, then do so.
543 * WARNING: This may leak information about the
546 if (t->use_tunneled_reply) {
547 pairadd(&request->reply->vps, fake->reply->vps);
548 fake->reply->vps = NULL;
552 case PW_AUTHENTICATION_REJECT:
553 DEBUG2(" PEAP: Tunneled authentication was rejected.");
554 t->status = PEAP_STATUS_SENT_TLV_FAILURE;
555 eappeap_failure(handler, tls_session);
556 rcode = RLM_MODULE_HANDLED;
559 case PW_ACCESS_CHALLENGE:
560 DEBUG2(" PEAP: Got tunneled Access-Challenge");
563 * Keep the State attribute, if necessary.
565 * Get rid of the old State, too.
568 pairmove2(&t->state, &fake->reply->vps, PW_STATE);
571 * We should really be a bit smarter about this,
572 * and move over only those attributes which
573 * are relevant to the authentication request,
574 * but that's a lot more work, and this "dumb"
575 * method works in 99.9% of the situations.
578 pairmove2(&vp, &fake->reply->vps, PW_EAP_MESSAGE);
581 * There MUST be a Reply-Message in the challenge,
582 * which we tunnel back to the client.
584 * If there isn't one in the reply VP's, then
585 * we MUST create one, with an empty string as
588 pairmove2(&vp, &fake->reply->vps, PW_REPLY_MESSAGE);
591 * Handle the ACK, by tunneling any necessary reply
592 * VP's back to the client.
595 vp2eap(tls_session, vp);
598 rcode = RLM_MODULE_HANDLED;
603 DEBUG2(" PEAP: Unknown RADIUS packet type %d: rejecting tunneled user", fake->reply->code);
604 rcode = RLM_MODULE_REJECT;