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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2003 Alan DeKok <aland@freeradius.org>
25 * LEAP Packet Format in EAP Type-Data
26 * --- ------ ------ -- --- ---------
28 * 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
29 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 * | Type 0x11 | Version 0x01 | Unused 0x00 | Count 0x08 |
31 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-
39 * Count is 8 octets since the Peer challenge is 8 bytes.
40 * Count is 24 for EAP response, with MSCHAP response.
41 * Length is the total number of octets in the EAP-Message.
43 * The LEAP type (0x11) is *not* included in the type data...
52 * Allocate a new LEAP_PACKET
54 LEAP_PACKET *eapleap_alloc(void)
58 if ((rp = malloc(sizeof(LEAP_PACKET))) == NULL) {
59 radlog(L_ERR, "rlm_eap_leap: out of memory");
62 memset(rp, 0, sizeof(LEAP_PACKET));
69 void eapleap_free(LEAP_PACKET **leap_packet_ptr)
71 LEAP_PACKET *leap_packet;
73 if (!leap_packet_ptr) return;
74 leap_packet = *leap_packet_ptr;
75 if (leap_packet == NULL) return;
77 if (leap_packet->challenge) free(leap_packet->challenge);
78 if (leap_packet->name) free(leap_packet->name);
82 *leap_packet_ptr = NULL;
86 * Extract the data from the LEAP packet.
88 LEAP_PACKET *eapleap_extract(EAP_DS *eap_ds)
95 * LEAP can have EAP-Response or EAP-Request (step 5)
96 * messages sent to it.
100 ((eap_ds->response->code != PW_EAP_RESPONSE) &&
101 (eap_ds->response->code != PW_EAP_REQUEST)) ||
102 eap_ds->response->type.type != PW_EAP_LEAP ||
103 !eap_ds->response->type.data ||
104 (eap_ds->response->length < LEAP_HEADER_LEN) ||
105 (eap_ds->response->type.data[0] != 0x01)) { /* version 1 */
106 radlog(L_ERR, "rlm_eap_leap: corrupted data");
111 * Hmm... this cast isn't the best thing to do.
113 data = (leap_packet_t *)eap_ds->response->type.data;
116 * Some simple sanity checks on the incoming packet.
118 * See 'leap.txt' in this directory for a description
121 switch (eap_ds->response->code) {
122 case PW_EAP_RESPONSE:
123 if (data->count != 24) {
124 radlog(L_ERR, "rlm_eap_leap: Bad NTChallengeResponse in LEAP stage 3");
130 if (data->count != 8) {
131 radlog(L_ERR, "rlm_eap_leap: Bad AP Challenge in LEAP stage 5");
137 radlog(L_ERR, "rlm_eap_leap: Invalid EAP code %d",
138 eap_ds->response->code);
143 packet = eapleap_alloc();
144 if (!packet) return NULL;
147 * Remember code, length, and id.
149 packet->code = eap_ds->response->code;
150 packet->id = eap_ds->response->id;
153 * The size of the LEAP portion of the packet, not
154 * counting the EAP header and the type.
156 packet->length = eap_ds->response->length - EAP_HEADER_LEN - 1;
159 * Remember the length of the challenge.
161 packet->count = data->count;
163 packet->challenge = malloc(packet->count);
164 if (packet->challenge == NULL) {
165 radlog(L_ERR, "rlm_eap_leap: out of memory");
166 eapleap_free(&packet);
169 memcpy(packet->challenge, data->challenge, packet->count);
172 * The User-Name comes after the challenge.
174 name_len = eap_ds->response->length - 3 - packet->count;
176 packet->name = malloc(name_len + 1);
178 radlog(L_ERR, "rlm_eap_leap: out of memory");
179 eapleap_free(&packet);
182 memcpy(packet->name, &data->challenge[packet->count],
184 packet->name[name_len] = '\0';
185 packet->name_len = name_len;
192 * Verify the MS-CHAP response from the user.
194 int eapleap_stage4(LEAP_PACKET *packet, VALUE_PAIR* password,
195 leap_session_t *session)
198 unsigned char unicode[512];
199 unsigned char ntpwdhash[16];
200 unsigned char response[24];
204 * No password or previous packet. Die.
206 if ((password == NULL) || (session == NULL)) {
210 if (password->attribute == PW_PASSWORD) {
212 * Convert the password to NT's weird Unicode format.
214 memset(unicode, 0, sizeof(unicode));
215 for (i = 0; i < password->length; i++) {
217 * Yes, the *even* bytes have the values,
218 * and the *odd* bytes are zero.
220 unicode[(i << 1)] = password->strvalue[i];
224 * Get the NT Password hash.
226 md4_calc(ntpwdhash, unicode, password->length * 2);
229 memcpy(ntpwdhash, password->strvalue, 16);
233 * Calculate and verify the CHAP challenge.
235 lrad_mschap(ntpwdhash, session->peer_challenge, response);
236 if (memcmp(response, packet->challenge, 24) == 0) {
237 DEBUG2(" rlm_eap_leap: NtChallengeResponse from AP is valid");
238 memcpy(session->peer_response, response, sizeof(response));
242 DEBUG2(" rlm_eap_leap: FAILED incorrect NtChallengeResponse from AP");
247 * Verify ourselves to the AP
249 LEAP_PACKET *eapleap_stage6(LEAP_PACKET *packet, REQUEST *request,
250 VALUE_PAIR *user_name, VALUE_PAIR* password,
251 leap_session_t *session, VALUE_PAIR **reply_vps)
254 unsigned char unicode[512];
255 unsigned char ntpwdhash[16];
261 * No password or previous packet. Die.
263 if ((password == NULL) || (session == NULL)) {
268 * Convert the password to NT's weird Unicode format.
270 memset(unicode, 0, sizeof(unicode));
271 for (i = 0; i < password->length; i++) {
273 * Yes, the *even* bytes have the values,
274 * and the *odd* bytes are zero.
276 unicode[(i << 1)] = password->strvalue[i];
279 reply = eapleap_alloc();
280 if (!reply) return NULL;
282 reply->code = PW_EAP_RESPONSE;
283 reply->length = LEAP_HEADER_LEN + 24 + user_name->length;
286 reply->challenge = malloc(reply->count);
287 if (reply->challenge == NULL) {
288 radlog(L_ERR, "rlm_eap_leap: out of memory");
289 eapleap_free(&reply);
294 * The LEAP packet also contains the user name.
296 reply->name = malloc(user_name->length + 1);
297 if (reply->name == NULL) {
298 radlog(L_ERR, "rlm_eap_leap: out of memory");
299 eapleap_free(&reply);
304 * Copy the name over, and ensure it's NUL terminated.
306 memcpy(reply->name, user_name->strvalue, user_name->length);
307 reply->name[user_name->length] = '\0';
308 reply->name_len = user_name->length;
311 * MPPE hash = ntpwdhash(ntpwdhash(unicode(pw)))
313 md4_calc(ntpwdhash, unicode, password->length * 2);
314 memcpy(unicode, ntpwdhash, 16);
315 md4_calc(ntpwdhash, unicode, 16);
318 * Calculate our response, to authenticate ourselves
321 lrad_mschap(ntpwdhash, packet->challenge, reply->challenge);
324 * Calculate the leap:session-key attribute
326 vp = pairmake("Cisco-AVPair", "leap:session-key=", T_OP_ADD);
328 radlog(L_ERR, "rlm_eap_leap: Failed to create Cisco-AVPair attribute. LEAP cancelled.");
329 eapleap_free(&reply);
334 * And calculate the MPPE session key.
337 memcpy(p, ntpwdhash, 16); /* MPPEHASH */
339 memcpy(p, packet->challenge, 8); /* APC */
341 memcpy(p, reply->challenge, 24); /* APR */
343 memcpy(p, session->peer_challenge, 8); /* PC */
345 memcpy(p, session->peer_response, 24); /* PR */
349 * These 16 bytes are the session key to use.
351 librad_md5_calc(ntpwdhash, unicode, 16 + 8 + 24 + 8 + 24);
353 memcpy(vp->strvalue + vp->length, ntpwdhash, 16);
354 memset(vp->strvalue + vp->length + 16, 0,
355 sizeof(vp->strvalue) - (vp->length + 16));
358 rad_tunnel_pwencode(vp->strvalue + vp->length, &i,
359 request->secret, request->packet->vector);
361 pairadd(reply_vps, vp);
367 * If an EAP LEAP request needs to be initiated then
368 * create such a packet.
370 LEAP_PACKET *eapleap_initiate(EAP_DS *eap_ds, VALUE_PAIR *user_name)
375 reply = eapleap_alloc();
377 radlog(L_ERR, "rlm_eap_leap: out of memory");
381 reply->code = PW_EAP_REQUEST;
382 reply->length = LEAP_HEADER_LEN + 8 + user_name->length;
383 reply->count = 8; /* random challenge */
385 reply->challenge = malloc(reply->count);
386 if (reply->challenge == NULL) {
387 radlog(L_ERR, "rlm_eap_leap: out of memory");
388 eapleap_free(&reply);
393 * Fill the challenge with random bytes.
395 for (i = 0; i < reply->count; i++) {
396 reply->challenge[i] = lrad_rand();
399 DEBUG2(" rlm_eap_leap: Issuing AP Challenge");
402 * The LEAP packet also contains the user name.
404 reply->name = malloc(user_name->length + 1);
405 if (reply->name == NULL) {
406 radlog(L_ERR, "rlm_eap_leap: out of memory");
407 eapleap_free(&reply);
412 * Copy the name over, and ensure it's NUL terminated.
414 memcpy(reply->name, user_name->strvalue, user_name->length);
415 reply->name[user_name->length] = '\0';
416 reply->name_len = user_name->length;
422 * compose the LEAP reply packet in the EAP reply typedata
424 int eapleap_compose(EAP_DS *eap_ds, LEAP_PACKET *reply)
429 * We need the name and the challenge.
431 switch (reply->code) {
433 case PW_EAP_RESPONSE:
434 eap_ds->request->type.type = PW_EAP_LEAP;
435 eap_ds->request->type.length = reply->length;
437 eap_ds->request->type.data = malloc(reply->length);
438 if (eap_ds->request->type.data == NULL) {
439 radlog(L_ERR, "rlm_eap_leap: out of memory");
442 data = (leap_packet_t *) eap_ds->request->type.data;
443 data->version = 0x01;
445 data->count = reply->count;
448 * N bytes of the challenge, followed by the user name.
450 memcpy(&data->challenge[0], reply->challenge, reply->count);
451 memcpy(&data->challenge[reply->count],
452 reply->name, reply->name_len);
456 * EAP-Success packets don't contain any data
457 * other than the header.
460 eap_ds->request->type.length = 0;
464 radlog(L_ERR, "rlm_eap_leap: Internal sanity check failed");
472 eap_ds->request->code = reply->code;