2 * eap_leap.c EAP LEAP functionality.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2003 Alan DeKok <aland@freeradius.org>
21 * Copyright 2006 The FreeRADIUS server project
26 * LEAP Packet Format in EAP Type-Data
27 * --- ------ ------ -- --- ---------
29 * 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
30 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31 * | Type 0x11 | Version 0x01 | Unused 0x00 | Count 0x08 |
32 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-
40 * Count is 8 octets since the Peer challenge is 8 bytes.
41 * Count is 24 for EAP response, with MSCHAP response.
42 * Length is the total number of octets in the EAP-Message.
44 * The LEAP type (0x11) is *not* included in the type data...
47 #include <freeradius-devel/ident.h>
56 * Allocate a new LEAP_PACKET
58 LEAP_PACKET *eapleap_alloc(void)
62 if ((rp = malloc(sizeof(LEAP_PACKET))) == NULL) {
63 radlog(L_ERR, "rlm_eap_leap: out of memory");
66 memset(rp, 0, sizeof(LEAP_PACKET));
73 void eapleap_free(LEAP_PACKET **leap_packet_ptr)
75 LEAP_PACKET *leap_packet;
77 if (!leap_packet_ptr) return;
78 leap_packet = *leap_packet_ptr;
79 if (leap_packet == NULL) return;
81 if (leap_packet->challenge) free(leap_packet->challenge);
82 if (leap_packet->name) free(leap_packet->name);
86 *leap_packet_ptr = NULL;
90 * Extract the data from the LEAP packet.
92 LEAP_PACKET *eapleap_extract(EAP_DS *eap_ds)
99 * LEAP can have EAP-Response or EAP-Request (step 5)
100 * messages sent to it.
104 ((eap_ds->response->code != PW_EAP_RESPONSE) &&
105 (eap_ds->response->code != PW_EAP_REQUEST)) ||
106 eap_ds->response->type.type != PW_EAP_LEAP ||
107 !eap_ds->response->type.data ||
108 (eap_ds->response->length < LEAP_HEADER_LEN) ||
109 (eap_ds->response->type.data[0] != 0x01)) { /* version 1 */
110 radlog(L_ERR, "rlm_eap_leap: corrupted data");
115 * Hmm... this cast isn't the best thing to do.
117 data = (leap_packet_t *)eap_ds->response->type.data;
120 * Some simple sanity checks on the incoming packet.
122 * See 'leap.txt' in this directory for a description
125 switch (eap_ds->response->code) {
126 case PW_EAP_RESPONSE:
127 if (data->count != 24) {
128 radlog(L_ERR, "rlm_eap_leap: Bad NTChallengeResponse in LEAP stage 3");
134 if (data->count != 8) {
135 radlog(L_ERR, "rlm_eap_leap: Bad AP Challenge in LEAP stage 5");
141 radlog(L_ERR, "rlm_eap_leap: Invalid EAP code %d",
142 eap_ds->response->code);
147 packet = eapleap_alloc();
148 if (!packet) return NULL;
151 * Remember code, length, and id.
153 packet->code = eap_ds->response->code;
154 packet->id = eap_ds->response->id;
157 * The size of the LEAP portion of the packet, not
158 * counting the EAP header and the type.
160 packet->length = eap_ds->response->length - EAP_HEADER_LEN - 1;
163 * Remember the length of the challenge.
165 packet->count = data->count;
167 packet->challenge = malloc(packet->count);
168 if (packet->challenge == NULL) {
169 radlog(L_ERR, "rlm_eap_leap: out of memory");
170 eapleap_free(&packet);
173 memcpy(packet->challenge, data->challenge, packet->count);
176 * The User-Name comes after the challenge.
178 * Length of the EAP-LEAP portion of the packet, minus
179 * 3 octets for data, minus the challenge size, is the
180 * length of the user name.
182 name_len = packet->length - 3 - packet->count;
184 packet->name = malloc(name_len + 1);
186 radlog(L_ERR, "rlm_eap_leap: out of memory");
187 eapleap_free(&packet);
190 memcpy(packet->name, &data->challenge[packet->count],
192 packet->name[name_len] = '\0';
193 packet->name_len = name_len;
200 * Get the NT-Password hash.
202 static int eapleap_ntpwdhash(unsigned char *ntpwdhash, VALUE_PAIR *password)
204 if ((password->attribute == PW_USER_PASSWORD) ||
205 (password->attribute == PW_CLEARTEXT_PASSWORD)) {
207 unsigned char unicode[512];
210 * Convert the password to NT's weird Unicode format.
212 memset(unicode, 0, sizeof(unicode));
213 for (i = 0; i < password->length; i++) {
215 * Yes, the *even* bytes have the values,
216 * and the *odd* bytes are zero.
218 unicode[(i << 1)] = password->vp_strvalue[i];
222 * Get the NT Password hash.
224 fr_md4_calc(ntpwdhash, unicode, password->length * 2);
226 } else { /* MUST be NT-Password */
227 if (password->length == 32) {
228 password->length = fr_hex2bin(password->vp_strvalue,
232 if (password->length != 16) {
233 radlog(L_ERR, "rlm_eap_leap: Bad NT-Password");
237 memcpy(ntpwdhash, password->vp_strvalue, 16);
244 * Verify the MS-CHAP response from the user.
246 int eapleap_stage4(LEAP_PACKET *packet, VALUE_PAIR* password,
247 leap_session_t *session)
249 unsigned char ntpwdhash[16];
250 unsigned char response[24];
254 * No password or previous packet. Die.
256 if ((password == NULL) || (session == NULL)) {
260 if (!eapleap_ntpwdhash(ntpwdhash, password)) {
265 * Calculate and verify the CHAP challenge.
267 eapleap_mschap(ntpwdhash, session->peer_challenge, response);
268 if (memcmp(response, packet->challenge, 24) == 0) {
269 DEBUG2(" rlm_eap_leap: NtChallengeResponse from AP is valid");
270 memcpy(session->peer_response, response, sizeof(response));
274 DEBUG2(" rlm_eap_leap: FAILED incorrect NtChallengeResponse from AP");
279 * Verify ourselves to the AP
281 LEAP_PACKET *eapleap_stage6(LEAP_PACKET *packet, REQUEST *request,
282 VALUE_PAIR *user_name, VALUE_PAIR* password,
283 leap_session_t *session, VALUE_PAIR **reply_vps)
286 unsigned char ntpwdhash[16], ntpwdhashhash[16];
287 unsigned char buffer[256];
293 * No password or previous packet. Die.
295 if ((password == NULL) || (session == NULL)) {
299 reply = eapleap_alloc();
300 if (!reply) return NULL;
302 reply->code = PW_EAP_RESPONSE;
303 reply->length = LEAP_HEADER_LEN + 24 + user_name->length;
306 reply->challenge = malloc(reply->count);
307 if (reply->challenge == NULL) {
308 radlog(L_ERR, "rlm_eap_leap: out of memory");
309 eapleap_free(&reply);
314 * The LEAP packet also contains the user name.
316 reply->name = malloc(user_name->length + 1);
317 if (reply->name == NULL) {
318 radlog(L_ERR, "rlm_eap_leap: out of memory");
319 eapleap_free(&reply);
324 * Copy the name over, and ensure it's NUL terminated.
326 memcpy(reply->name, user_name->vp_strvalue, user_name->length);
327 reply->name[user_name->length] = '\0';
328 reply->name_len = user_name->length;
331 * MPPE hash = ntpwdhash(ntpwdhash(unicode(pw)))
333 if (!eapleap_ntpwdhash(ntpwdhash, password)) {
334 eapleap_free(&reply);
337 fr_md4_calc(ntpwdhashhash, ntpwdhash, 16);
340 * Calculate our response, to authenticate ourselves
343 eapleap_mschap(ntpwdhashhash, packet->challenge, reply->challenge);
346 * Calculate the leap:session-key attribute
348 vp = pairmake("Cisco-AVPair", "leap:session-key=", T_OP_ADD);
350 radlog(L_ERR, "rlm_eap_leap: Failed to create Cisco-AVPair attribute. LEAP cancelled.");
351 eapleap_free(&reply);
356 * And calculate the MPPE session key.
359 memcpy(p, ntpwdhashhash, 16); /* MPPEHASH */
361 memcpy(p, packet->challenge, 8); /* APC */
363 memcpy(p, reply->challenge, 24); /* APR */
365 memcpy(p, session->peer_challenge, 8); /* PC */
367 memcpy(p, session->peer_response, 24); /* PR */
370 * These 16 bytes are the session key to use.
372 fr_md5_calc(ntpwdhash, buffer, 16 + 8 + 24 + 8 + 24);
374 memcpy(vp->vp_strvalue + vp->length, ntpwdhash, 16);
375 memset(vp->vp_strvalue + vp->length + 16, 0,
376 sizeof(vp->vp_strvalue) - (vp->length + 16));
379 rad_tunnel_pwencode(vp->vp_strvalue + vp->length, &i,
380 request->client->secret, request->packet->vector);
382 pairadd(reply_vps, vp);
388 * If an EAP LEAP request needs to be initiated then
389 * create such a packet.
391 LEAP_PACKET *eapleap_initiate(UNUSED EAP_DS *eap_ds, VALUE_PAIR *user_name)
396 reply = eapleap_alloc();
398 radlog(L_ERR, "rlm_eap_leap: out of memory");
402 reply->code = PW_EAP_REQUEST;
403 reply->length = LEAP_HEADER_LEN + 8 + user_name->length;
404 reply->count = 8; /* random challenge */
406 reply->challenge = malloc(reply->count);
407 if (reply->challenge == NULL) {
408 radlog(L_ERR, "rlm_eap_leap: out of memory");
409 eapleap_free(&reply);
414 * Fill the challenge with random bytes.
416 for (i = 0; i < reply->count; i++) {
417 reply->challenge[i] = fr_rand();
420 DEBUG2(" rlm_eap_leap: Issuing AP Challenge");
423 * The LEAP packet also contains the user name.
425 reply->name = malloc(user_name->length + 1);
426 if (reply->name == NULL) {
427 radlog(L_ERR, "rlm_eap_leap: out of memory");
428 eapleap_free(&reply);
433 * Copy the name over, and ensure it's NUL terminated.
435 memcpy(reply->name, user_name->vp_strvalue, user_name->length);
436 reply->name[user_name->length] = '\0';
437 reply->name_len = user_name->length;
443 * compose the LEAP reply packet in the EAP reply typedata
445 int eapleap_compose(EAP_DS *eap_ds, LEAP_PACKET *reply)
450 * We need the name and the challenge.
452 switch (reply->code) {
454 case PW_EAP_RESPONSE:
455 eap_ds->request->type.type = PW_EAP_LEAP;
456 eap_ds->request->type.length = reply->length;
458 eap_ds->request->type.data = malloc(reply->length);
459 if (eap_ds->request->type.data == NULL) {
460 radlog(L_ERR, "rlm_eap_leap: out of memory");
463 data = (leap_packet_t *) eap_ds->request->type.data;
464 data->version = 0x01;
466 data->count = reply->count;
469 * N bytes of the challenge, followed by the user name.
471 memcpy(&data->challenge[0], reply->challenge, reply->count);
472 memcpy(&data->challenge[reply->count],
473 reply->name, reply->name_len);
477 * EAP-Success packets don't contain any data
478 * other than the header.
481 eap_ds->request->type.length = 0;
485 radlog(L_ERR, "rlm_eap_leap: Internal sanity check failed");
493 eap_ds->request->code = reply->code;