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...
54 * Extract the data from the LEAP packet.
56 leap_packet_t *eapleap_extract(EAP_DS *eap_ds)
58 leap_packet_raw_t *data;
59 leap_packet_t *packet;
63 * LEAP can have EAP-Response or EAP-Request (step 5)
64 * messages sent to it.
68 ((eap_ds->response->code != PW_EAP_RESPONSE) &&
69 (eap_ds->response->code != PW_EAP_REQUEST)) ||
70 eap_ds->response->type.num != PW_EAP_LEAP ||
71 !eap_ds->response->type.data ||
72 (eap_ds->response->length < LEAP_HEADER_LEN) ||
73 (eap_ds->response->type.data[0] != 0x01)) { /* version 1 */
74 ERROR("rlm_eap_leap: corrupted data");
79 * Hmm... this cast isn't the best thing to do.
81 data = (leap_packet_raw_t *)eap_ds->response->type.data;
84 * Some simple sanity checks on the incoming packet.
86 * See 'leap.txt' in this directory for a description
89 switch (eap_ds->response->code) {
91 if (data->count != 24) {
92 ERROR("rlm_eap_leap: Bad NTChallengeResponse in LEAP stage 3");
98 if (data->count != 8) {
99 ERROR("rlm_eap_leap: Bad AP Challenge in LEAP stage 5");
105 ERROR("rlm_eap_leap: Invalid EAP code %d",
106 eap_ds->response->code);
111 packet = talloc(eap_ds, leap_packet_t);
112 if (!packet) return NULL;
115 * Remember code, length, and id.
117 packet->code = eap_ds->response->code;
118 packet->id = eap_ds->response->id;
121 * The size of the LEAP portion of the packet, not
122 * counting the EAP header and the type.
124 packet->length = eap_ds->response->length - EAP_HEADER_LEN - 1;
127 * Remember the length of the challenge.
129 packet->count = data->count;
131 packet->challenge = talloc_array(packet, uint8_t, packet->count);
132 if (!packet->challenge) {
136 memcpy(packet->challenge, data->challenge, packet->count);
139 * The User-Name comes after the challenge.
141 * Length of the EAP-LEAP portion of the packet, minus
142 * 3 octets for data, minus the challenge size, is the
143 * length of the user name.
145 name_len = packet->length - 3 - packet->count;
147 packet->name = talloc_array(packet, char, name_len + 1);
152 memcpy(packet->name, &data->challenge[packet->count],
154 packet->name[name_len] = '\0';
155 packet->name_len = name_len;
162 * Get the NT-Password hash.
164 static int eapleap_ntpwdhash(unsigned char *ntpwdhash, VALUE_PAIR *password)
166 if ((password->da->attr == PW_USER_PASSWORD) ||
167 (password->da->attr == PW_CLEARTEXT_PASSWORD)) {
169 unsigned char unicode[512];
172 * Convert the password to NT's weird Unicode format.
174 memset(unicode, 0, sizeof(unicode));
175 for (i = 0; i < password->length; i++) {
177 * Yes, the *even* bytes have the values,
178 * and the *odd* bytes are zero.
180 unicode[(i << 1)] = password->vp_strvalue[i];
184 * Get the NT Password hash.
186 fr_md4_calc(ntpwdhash, unicode, password->length * 2);
188 } else { /* MUST be NT-Password */
191 if (password->length == 32) {
192 p = talloc_array(password, uint8_t, 16);
193 password->length = fr_hex2bin(p, password->vp_strvalue, 16);
195 if (password->length != 16) {
196 ERROR("rlm_eap_leap: Bad NT-Password");
201 pairmemcpy(password, p, 16);
205 memcpy(ntpwdhash, password->vp_octets, 16);
212 * Verify the MS-CHAP response from the user.
214 int eapleap_stage4(leap_packet_t *packet, VALUE_PAIR* password,
215 leap_session_t *session)
217 unsigned char ntpwdhash[16];
218 unsigned char response[24];
222 * No password or previous packet. Die.
224 if ((!password) || (!session)) {
228 if (!eapleap_ntpwdhash(ntpwdhash, password)) {
233 * Calculate and verify the CHAP challenge.
235 eapleap_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_t *eapleap_stage6(leap_packet_t *packet, REQUEST *request,
250 VALUE_PAIR *user_name, VALUE_PAIR* password,
251 leap_session_t *session)
254 uint8_t ntpwdhash[16], ntpwdhashhash[16];
255 uint8_t *p, buffer[256];
256 leap_packet_t *reply;
261 * No password or previous packet. Die.
263 if ((!password) || (!session)) {
267 reply = talloc(session, leap_packet_t);
268 if (!reply) return NULL;
270 reply->code = PW_EAP_RESPONSE;
271 reply->length = LEAP_HEADER_LEN + 24 + user_name->length;
274 reply->challenge = talloc_array(reply, uint8_t, reply->count);
275 if (!reply->challenge) {
281 * The LEAP packet also contains the user name.
283 reply->name = talloc_array(reply, char, user_name->length + 1);
290 * Copy the name over, and ensure it's NUL terminated.
292 memcpy(reply->name, user_name->vp_strvalue, user_name->length);
293 reply->name[user_name->length] = '\0';
294 reply->name_len = user_name->length;
297 * MPPE hash = ntpwdhash(ntpwdhash(unicode(pw)))
299 if (!eapleap_ntpwdhash(ntpwdhash, password)) {
303 fr_md4_calc(ntpwdhashhash, ntpwdhash, 16);
306 * Calculate our response, to authenticate ourselves
309 eapleap_mschap(ntpwdhashhash, packet->challenge, reply->challenge);
312 * Calculate the leap:session-key attribute
314 vp = pairmake_reply("Cisco-AVPair", NULL, T_OP_ADD);
316 ERROR("rlm_eap_leap: Failed to create Cisco-AVPair attribute. LEAP cancelled.");
322 * And calculate the MPPE session key.
325 memcpy(p, ntpwdhashhash, 16); /* MPPEHASH */
327 memcpy(p, packet->challenge, 8); /* APC */
329 memcpy(p, reply->challenge, 24); /* APR */
331 memcpy(p, session->peer_challenge, 8); /* PC */
333 memcpy(p, session->peer_response, 24); /* PR */
336 * These 16 bytes are the session key to use.
338 fr_md5_calc(ntpwdhash, buffer, 16 + 8 + 24 + 8 + 24);
340 q = talloc_array(vp, char, 16 + sizeof("leap:session-key="));
341 strcpy(q, "leap:session-key=");
343 memcpy(q + 17, ntpwdhash, 16);
346 rad_tunnel_pwencode(q + 17, &i,
347 request->client->secret, request->packet->vector);
349 // talloc_free(vp->vp_strvalue);
357 * If an EAP LEAP request needs to be initiated then
358 * create such a packet.
360 leap_packet_t *eapleap_initiate(EAP_DS *eap_ds, VALUE_PAIR *user_name)
363 leap_packet_t *reply;
365 reply = talloc(eap_ds, leap_packet_t);
370 reply->code = PW_EAP_REQUEST;
371 reply->length = LEAP_HEADER_LEN + 8 + user_name->length;
372 reply->count = 8; /* random challenge */
374 reply->challenge = talloc_array(reply, uint8_t, reply->count);
375 if (!reply->challenge) {
381 * Fill the challenge with random bytes.
383 for (i = 0; i < reply->count; i++) {
384 reply->challenge[i] = fr_rand();
387 DEBUG2(" rlm_eap_leap: Issuing AP Challenge");
390 * The LEAP packet also contains the user name.
392 reply->name = talloc_array(reply, char, user_name->length + 1);
399 * Copy the name over, and ensure it's NUL terminated.
401 memcpy(reply->name, user_name->vp_strvalue, user_name->length);
402 reply->name[user_name->length] = '\0';
403 reply->name_len = user_name->length;
409 * compose the LEAP reply packet in the EAP reply typedata
411 int eapleap_compose(EAP_DS *eap_ds, leap_packet_t *reply)
413 leap_packet_raw_t *data;
416 * We need the name and the challenge.
418 switch (reply->code) {
420 case PW_EAP_RESPONSE:
421 eap_ds->request->type.num = PW_EAP_LEAP;
422 eap_ds->request->type.length = reply->length;
424 eap_ds->request->type.data = talloc_array(eap_ds->request,
427 if (!eap_ds->request->type.data) {
430 data = (leap_packet_raw_t *) eap_ds->request->type.data;
431 data->version = 0x01;
433 data->count = reply->count;
436 * N bytes of the challenge, followed by the user name.
438 memcpy(&data->challenge[0], reply->challenge, reply->count);
439 memcpy(&data->challenge[reply->count],
440 reply->name, reply->name_len);
444 * EAP-Success packets don't contain any data
445 * other than the header.
448 eap_ds->request->type.length = 0;
452 ERROR("rlm_eap_leap: Internal sanity check failed");
460 eap_ds->request->code = reply->code;