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.");
256 * Use a reply packet to determine what to do.
258 static int process_reply(EAP_HANDLER *handler, tls_session_t *tls_session,
259 REQUEST *request, RADIUS_PACKET *reply)
261 int rcode = RLM_MODULE_REJECT;
263 peap_tunnel_t *t = tls_session->opaque;
265 switch (reply->code) {
266 case PW_AUTHENTICATION_ACK:
267 DEBUG2(" PEAP: Tunneled authentication was successful.");
268 t->status = PEAP_STATUS_SENT_TLV_SUCCESS;
269 eappeap_success(handler, tls_session);
270 rcode = RLM_MODULE_OK;
273 * If we've been told to use the attributes from
274 * the reply, then do so.
276 * WARNING: This may leak information about the
279 if (t->use_tunneled_reply) {
280 pairadd(&request->reply->vps, reply->vps);
285 case PW_AUTHENTICATION_REJECT:
286 DEBUG2(" PEAP: Tunneled authentication was rejected.");
287 t->status = PEAP_STATUS_SENT_TLV_FAILURE;
288 eappeap_failure(handler, tls_session);
289 rcode = RLM_MODULE_HANDLED;
292 case PW_ACCESS_CHALLENGE:
293 DEBUG2(" PEAP: Got tunneled Access-Challenge");
296 * Keep the State attribute, if necessary.
298 * Get rid of the old State, too.
301 pairmove2(&t->state, &(reply->vps), PW_STATE);
304 * PEAP takes only EAP-Message attributes inside
305 * of the tunnel. Any Reply-Message in the
306 * Access-Challenge is ignored.
309 pairmove2(&vp, &(reply->vps), PW_EAP_MESSAGE);
312 * Handle the ACK, by tunneling any necessary reply
313 * VP's back to the client.
316 vp2eap(tls_session, vp);
320 rcode = RLM_MODULE_HANDLED;
324 DEBUG2(" PEAP: Unknown RADIUS packet type %d: rejecting tunneled user", reply->code);
325 rcode = RLM_MODULE_REJECT;
334 * Do post-proxy processing,
336 static int eappeap_postproxy(EAP_HANDLER *handler, void *data)
339 tls_session_t *tls_session = (tls_session_t *) data;
341 DEBUG2(" PEAP: Passing reply from proxy back into the tunnel.");
344 * Process the reply from the home server.
346 rcode = process_reply(handler, tls_session, handler->request,
347 handler->request->proxy_reply);
350 * The proxy code uses the reply from the home server as
351 * the basis for the reply to the NAS. We don't want that,
352 * so we toss it, after we've had our way with it.
354 pairfree(&handler->request->proxy_reply->vps);
357 case RLM_MODULE_REJECT:
358 DEBUG2(" PEAP: Reply was rejected");
359 eaptls_fail(handler->eap_ds, 0);
362 case RLM_MODULE_HANDLED:
363 DEBUG2(" PEAP: Reply was handled");
364 eaptls_request(handler->eap_ds, tls_session);
368 DEBUG2(" PEAP: Reply was OK");
369 eaptls_success(handler->eap_ds, 0);
370 eaptls_gen_mppe_keys(&handler->request->reply->vps,
372 "client EAP encryption");
376 DEBUG2(" PEAP: Reply was unknown.");
380 eaptls_fail(handler->eap_ds, 0);
386 * Process the pseudo-EAP contents of the tunneled data.
388 int eappeap_process(EAP_HANDLER *handler, tls_session_t *tls_session)
391 peap_tunnel_t *t = tls_session->opaque;
394 int rcode = RLM_MODULE_REJECT;
396 unsigned int data_len;
397 unsigned char buffer[1024];
402 REQUEST *request = handler->request;
403 EAP_DS *eap_ds = handler->eap_ds;
406 * Grab the dirty data, and copy it to our buffer.
408 * I *really* don't like these 'record_t' things...
410 data_len = record_minus(&tls_session->dirty_in, buffer, sizeof(buffer));
414 * Write the data from the dirty buffer (i.e. packet
415 * data) into the buffer which we will give to SSL for
418 * Some of this code COULD technically go into the TLS
419 * module, in eaptls_process(), where it returns EAPTLS_OK.
421 * Similarly, the writing of data to the SSL context could
424 BIO_write(tls_session->into_ssl, buffer, data_len);
425 record_init(&tls_session->clean_out);
428 * Read (and decrypt) the tunneled data from the SSL session,
429 * and put it into the decrypted data buffer.
431 err = SSL_read(tls_session->ssl, tls_session->clean_out.data,
432 sizeof(tls_session->clean_out.data));
435 * FIXME: Call SSL_get_error() to see what went
438 radlog(L_INFO, "rlm_eap_peap: SSL_read Error");
439 return RLM_MODULE_REJECT;
443 * If there's no data, maybe this is an ACK to an
448 * FIXME: Call SSL_get_error() to see what went
451 radlog(L_INFO, "rlm_eap_peap: No data inside of the tunnel.");
452 return RLM_MODULE_REJECT;
455 data_len = tls_session->clean_out.used = err;
456 data = tls_session->clean_out.data;
459 if (debug_flag > 2) for (i = 0; i < data_len; i++) {
460 if ((i & 0x0f) == 0) printf(" PEAP tunnel data in %04x: ", i);
462 printf("%02x ", data[i]);
464 if ((i & 0x0f) == 0x0f) printf("\n");
466 if ((data_len & 0x0f) != 0) printf("\n");
469 if (!eapmessage_verify(data, data_len)) {
470 return RLM_MODULE_REJECT;
473 DEBUG2(" rlm_eap_peap: Tunneled data is valid.");
476 * If we authenticated the user, then it's OK.
478 if (t->status == PEAP_STATUS_SENT_TLV_SUCCESS) {
479 if (eappeap_check_tlv(data)) {
480 DEBUG2(" rlm_eap_peap: Success");
481 return RLM_MODULE_OK;
484 return RLM_MODULE_REJECT;
486 } else if (t->status == PEAP_STATUS_SENT_TLV_FAILURE) {
487 DEBUG2(" rlm_eap_peap: Had sent TLV failure, rejecting.");
488 return RLM_MODULE_REJECT;
491 fake = request_alloc_fake(request);
493 rad_assert(fake->packet->vps == NULL);
495 fake->packet->vps = eap2vp(eap_ds, data, data_len);
496 if (!fake->packet->vps) {
497 DEBUG2(" rlm_eap_peap: Unable to convert tunneled EAP packet to internal server data structures");
498 return PW_AUTHENTICATION_REJECT;
502 if (debug_flag > 0) {
503 printf(" PEAP: Got tunneled EAP-Message\n");
505 for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
506 putchar('\t');vp_print(stdout, vp);putchar('\n');
512 * Tell the request that it's a fake one.
514 vp = pairmake("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
516 pairadd(&fake->packet->vps, vp);
520 * Update other items in the REQUEST data structure.
523 if ((data[0] == PW_EAP_IDENTITY) && (data_len > 1)) {
524 t->username = pairmake("User-Name", "", T_OP_EQ);
525 rad_assert(t->username != NULL);
527 memcpy(t->username->strvalue, data+1, data_len - 1);
528 t->username->length = data_len -1;
529 t->username->strvalue[t->username->length] = 0;
530 DEBUG2(" PEAP: Got tunneled identity of %s", t->username->strvalue);
533 * If there's a default EAP type,
536 if (t->default_eap_type != 0) {
537 DEBUG2(" PEAP: Setting default EAP type for tunneled EAP session.");
538 vp = pairmake("EAP-Type", "0", T_OP_EQ);
539 vp->lvalue = t->default_eap_type;
540 pairadd(&fake->config_items, vp);
543 } /* else there WAS a t->username */
546 vp = paircopy(t->username);
547 pairadd(&fake->packet->vps, vp);
548 fake->username = pairfind(fake->packet->vps, PW_USER_NAME);
552 * Add the State attribute, too, if it exists.
555 DEBUG2(" PEAP: Adding old state with %02x %02x",
556 t->state->strvalue[0], t->state->strvalue[1]);
557 vp = paircopy(t->state);
558 if (vp) pairadd(&fake->packet->vps, vp);
562 * If this is set, we copy SOME of the request attributes
563 * from outside of the tunnel to inside of the tunnel.
565 * We copy ONLY those attributes which do NOT already
566 * exist in the tunneled request.
568 * This code is copied from ../rlm_eap_ttls/ttls.c
570 if (t->copy_request_to_tunnel) {
573 for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
575 * The attribute is a server-side thingy,
578 if ((vp->attribute > 255) &&
579 (((vp->attribute >> 16) & 0xffff) == 0)) {
584 * The outside attribute is already in the
585 * tunnel, don't copy it.
587 * This works for BOTH attributes which
588 * are originally in the tunneled request,
589 * AND attributes which are copied there
592 if (pairfind(fake->packet->vps, vp->attribute)) {
597 * Some attributes are handled specially.
599 switch (vp->attribute) {
601 * NEVER copy Message-Authenticator,
602 * EAP-Message, or State. They're
603 * only for outside of the tunnel.
606 case PW_USER_PASSWORD:
607 case PW_CHAP_PASSWORD:
608 case PW_CHAP_CHALLENGE:
610 case PW_MESSAGE_AUTHENTICATOR:
617 * By default, copy it over.
624 * Don't copy from the head, we've already
627 copy = paircopy2(vp, vp->attribute);
628 pairadd(&fake->packet->vps, copy);
633 if (debug_flag > 0) {
634 printf(" PEAP: Sending tunneled request\n");
636 for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
637 putchar('\t');vp_print(stdout, vp);putchar('\n');
643 * Call authentication recursively, which will
644 * do PAP, CHAP, MS-CHAP, etc.
646 rad_authenticate(fake);
649 * Note that we don't do *anything* with the reply
653 if (debug_flag > 0) {
654 printf(" PEAP: Got tunneled reply RADIUS code %d\n",
657 for (vp = fake->reply->vps; vp != NULL; vp = vp->next) {
658 putchar('\t');vp_print(stdout, vp);putchar('\n');
664 * Decide what to do with the reply.
666 switch (fake->reply->code) {
667 case 0: /* No reply code, must be proxied... */
668 vp = pairfind(fake->config_items, PW_PROXY_TO_REALM);
670 eap_tunnel_data_t *tunnel;
671 DEBUG2(" PEAP: Tunneled authentication will be proxied to %s", vp->strvalue);
674 * Tell the original request that it's going
677 pairmove2(&(request->config_items),
678 &(fake->config_items),
682 * Seed the proxy packet with the
685 rad_assert(request->proxy == NULL);
686 request->proxy = fake->packet;
690 * Set up the callbacks for the tunnel
692 tunnel = rad_malloc(sizeof(*tunnel));
693 memset(tunnel, 0, sizeof(*tunnel));
695 tunnel->tls_session = tls_session;
696 tunnel->callback = eappeap_postproxy;
699 * Associate the callback with the request.
701 rcode = request_data_add(request,
703 REQUEST_DATA_EAP_TUNNEL_CALLBACK,
705 rad_assert(rcode == 0);
708 * Didn't authenticate the packet, but
711 rcode = RLM_MODULE_UPDATED;
714 DEBUG2(" PEAP: Unknown RADIUS packet type %d: rejecting tunneled user", fake->reply->code);
715 rcode = RLM_MODULE_REJECT;
720 rcode = process_reply(handler, tls_session, request,