All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
eap_leap.c
Go to the documentation of this file.
1 /*
2  * eap_leap.c EAP LEAP functionality.
3  *
4  * Version: $Id: d81df779658b5ba773c3b8dbb49abddcc227aad0 $
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2003 Alan DeKok <aland@freeradius.org>
21  * Copyright 2006 The FreeRADIUS server project
22  */
23 
24 /*
25  *
26  * LEAP Packet Format in EAP Type-Data
27  * --- ------ ------ -- --- ---------
28  * 0 1 2 3
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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33  * | Peer Challenge |
34  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35  * | Peer Challenge |
36  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37  * | User Name .....
38  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-
39  *
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.
43  *
44  * The LEAP type (0x11) is *not* included in the type data...
45  */
46 
47 RCSID("$Id: d81df779658b5ba773c3b8dbb49abddcc227aad0 $")
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include "eap.h"
52 #include "eap_leap.h"
53 
54 #include <freeradius-devel/md5.h>
55 
56 /*
57  * Extract the data from the LEAP packet.
58  */
60 {
62  leap_packet_t *packet;
63  int name_len;
64 
65  /*
66  * LEAP can have EAP-Response or EAP-Request (step 5)
67  * messages sent to it.
68  */
69  if (!eap_round || !eap_round->response ||
70  ((eap_round->response->code != PW_EAP_RESPONSE) && (eap_round->response->code != PW_EAP_REQUEST)) ||
71  (eap_round->response->type.num != PW_EAP_LEAP) || !eap_round->response->type.data ||
72  (eap_round->response->length < LEAP_HEADER_LEN) ||
73  (eap_round->response->type.data[0] != 0x01)) { /* version 1 */
74  REDEBUG("Corrupted data");
75  return NULL;
76  }
77 
78  /*
79  * Hmm... this cast isn't the best thing to do.
80  */
81  data = (leap_packet_raw_t *)eap_round->response->type.data;
82 
83  /*
84  * Some simple sanity checks on the incoming packet.
85  *
86  * See 'leap.txt' in this directory for a description
87  * of the stages.
88  */
89  switch (eap_round->response->code) {
90  case PW_EAP_RESPONSE:
91  if (data->count != 24) {
92  REDEBUG("Bad NTChallengeResponse in LEAP stage 3");
93  return NULL;
94  }
95  break;
96 
97  case PW_EAP_REQUEST:
98  if (data->count != 8) {
99  REDEBUG("Bad AP Challenge in LEAP stage 5");
100  return NULL;
101  }
102  break;
103 
104  default:
105  REDEBUG("Invalid EAP code %d", eap_round->response->code);
106  return NULL;
107  }
108 
109  packet = talloc(eap_round, leap_packet_t);
110  if (!packet) return NULL;
111 
112  /*
113  * Remember code, length, and id.
114  */
115  packet->code = eap_round->response->code;
116  packet->id = eap_round->response->id;
117 
118  /*
119  * The size of the LEAP portion of the packet, not
120  * counting the EAP header and the type.
121  */
122  packet->length = eap_round->response->length - EAP_HEADER_LEN - 1;
123 
124  /*
125  * Remember the length of the challenge.
126  */
127  packet->count = data->count;
128 
129  packet->challenge = talloc_array(packet, uint8_t, packet->count);
130  if (!packet->challenge) {
131  talloc_free(packet);
132  return NULL;
133  }
134  memcpy(packet->challenge, data->challenge, packet->count);
135 
136  /*
137  * The User-Name comes after the challenge.
138  *
139  * Length of the EAP-LEAP portion of the packet, minus
140  * 3 octets for data, minus the challenge size, is the
141  * length of the user name.
142  */
143  name_len = packet->length - 3 - packet->count;
144  if (name_len > 0) {
145  packet->name = talloc_array(packet, char, name_len + 1);
146  if (!packet->name) {
147  talloc_free(packet);
148  return NULL;
149  }
150  memcpy(packet->name, &data->challenge[packet->count], name_len);
151  packet->name[name_len] = '\0';
152  packet->name_len = name_len;
153  }
154 
155  return packet;
156 }
157 
158 /*
159  * Get the NT-Password hash.
160  */
161 static int eap_leap_ntpwdhash(uint8_t *out, REQUEST *request, VALUE_PAIR *password)
162 {
163  if ((password->da->attr == PW_USER_PASSWORD) ||
164  (password->da->attr == PW_CLEARTEXT_PASSWORD)) {
165  ssize_t len;
166  uint8_t ucs2_password[512];
167 
168  /*
169  * Convert the password to NT's weird Unicode format.
170  */
171  len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), password->vp_strvalue, password->vp_length);
172  if (len < 0) {
173  REDEBUG("Error converting password to UCS2");
174  return 0;
175  }
176 
177  /*
178  * Get the NT Password hash.
179  */
180  fr_md4_calc(out, ucs2_password, len);
181  } else { /* MUST be NT-Password */
182  uint8_t *p = NULL;
183 
184  if (password->vp_length == 32) {
185  p = talloc_array(password, uint8_t, 16);
186  password->vp_length = fr_hex2bin(p, 16, password->vp_strvalue, password->vp_length);
187  }
188  if (password->vp_length != 16) {
189  REDEBUG("Bad NT-Password");
190  return 0;
191  }
192 
193  if (p) {
194  fr_pair_value_memcpy(password, p, 16);
195  talloc_free(p);
196  }
197 
198  memcpy(out, password->vp_octets, 16);
199  }
200  return 1;
201 }
202 
203 
204 /*
205  * Verify the MS-CHAP response from the user.
206  */
207 int eap_leap_stage4(REQUEST *request, leap_packet_t *packet, VALUE_PAIR *password, leap_session_t *session)
208 {
209  uint8_t hash[16];
210  uint8_t response[24];
211 
212  /*
213  * No password or previous packet. Die.
214  */
215  if ((!password) || (!session)) {
216  return 0;
217  }
218 
219  if (!eap_leap_ntpwdhash(hash, request, password)) {
220  return 0;
221  }
222 
223  /*
224  * Calculate and verify the CHAP challenge.
225  */
226  eap_leap_mschap(hash, session->peer_challenge, response);
227  if (memcmp(response, packet->challenge, 24) == 0) {
228  RDEBUG2("NTChallengeResponse from AP is valid");
229  memcpy(session->peer_response, response, sizeof(response));
230  return 1;
231  }
232  REDEBUG("FAILED incorrect NtChallengeResponse from AP");
233 
234  return 0;
235 }
236 
237 /*
238  * Verify ourselves to the AP
239  */
240 leap_packet_t *eap_leap_stage6(REQUEST *request, leap_packet_t *packet, VALUE_PAIR *user_name, VALUE_PAIR *password,
241  leap_session_t *session)
242 {
243  size_t i;
244  uint8_t hash[16], mppe[16];
245  uint8_t *p, buffer[256];
246  leap_packet_t *reply;
247  char *q;
248  VALUE_PAIR *vp;
249 
250  /*
251  * No password or previous packet. Die.
252  */
253  if ((!password) || (!session)) {
254  return NULL;
255  }
256 
257  reply = talloc(session, leap_packet_t);
258  if (!reply) return NULL;
259 
260  reply->code = PW_EAP_RESPONSE;
261  reply->length = LEAP_HEADER_LEN + 24 + user_name->vp_length;
262  reply->count = 24;
263 
264  reply->challenge = talloc_array(reply, uint8_t, reply->count);
265  if (!reply->challenge) {
266  talloc_free(reply);
267  return NULL;
268  }
269 
270  /*
271  * The LEAP packet also contains the user name.
272  */
273  reply->name = talloc_array(reply, char, user_name->vp_length + 1);
274  if (!reply->name) {
275  talloc_free(reply);
276  return NULL;
277  }
278 
279  /*
280  * Copy the name over, and ensure it's NUL terminated.
281  */
282  memcpy(reply->name, user_name->vp_strvalue, user_name->vp_length);
283  reply->name[user_name->vp_length] = '\0';
284  reply->name_len = user_name->vp_length;
285 
286  /*
287  * MPPE hash = ntpwdhash(ntpwdhash(unicode(pw)))
288  */
289  if (!eap_leap_ntpwdhash(hash, request, password)) {
290  talloc_free(reply);
291  return NULL;
292  }
293  fr_md4_calc(mppe, hash, 16);
294 
295  /*
296  * Calculate our response, to authenticate ourselves to the AP.
297  */
298  eap_leap_mschap(mppe, packet->challenge, reply->challenge);
299 
300  /*
301  * Calculate the leap:session-key attribute
302  */
303  vp = pair_make_reply("Cisco-AVPair", NULL, T_OP_ADD);
304  if (!vp) {
305  REDEBUG("Failed to create Cisco-AVPair attribute. LEAP cancelled");
306  talloc_free(reply);
307  return NULL;
308  }
309 
310  /*
311  * And calculate the MPPE session key.
312  */
313  p = buffer;
314  memcpy(p, mppe, 16); /* MPPEHASH */
315  p += 16;
316  memcpy(p, packet->challenge, 8); /* APC */
317  p += 8;
318  memcpy(p, reply->challenge, 24); /* APR */
319  p += 24;
320  memcpy(p, session->peer_challenge, 8); /* PC */
321  p += 8;
322  memcpy(p, session->peer_response, 24); /* PR */
323 
324  /*
325  * These 16 bytes are the session key to use.
326  */
327  fr_md5_calc(hash, buffer, 16 + 8 + 24 + 8 + 24);
328 
329  q = talloc_array(vp, char, FR_TUNNEL_PW_ENC_LENGTH(16) + sizeof("leap:session-key="));
330  strcpy(q, "leap:session-key=");
331 
332  memcpy(q + 17, hash, 16);
333 
334  i = 16;
335  fr_radius_encode_tunnel_password(q + 17, &i, request->client->secret, request->packet->vector);
336  fr_pair_value_strsteal(vp, q);
337  vp->vp_length = 17 + i;
338 
339  return reply;
340 }
341 
342 /*
343  * If an EAP LEAP request needs to be initiated then
344  * create such a packet.
345  */
347 {
348  int i;
349  leap_packet_t *reply;
350 
351  reply = talloc(eap_round, leap_packet_t);
352  if (!reply) {
353  return NULL;
354  }
355 
356  reply->code = PW_EAP_REQUEST;
357  reply->length = LEAP_HEADER_LEN + 8 + user_name->vp_length;
358  reply->count = 8; /* random challenge */
359 
360  reply->challenge = talloc_array(reply, uint8_t, reply->count);
361  if (!reply->challenge) {
362  talloc_free(reply);
363  return NULL;
364  }
365 
366  /*
367  * Fill the challenge with random bytes.
368  */
369  for (i = 0; i < reply->count; i++) {
370  reply->challenge[i] = fr_rand();
371  }
372  RDEBUG2("Issuing AP Challenge");
373 
374  /*
375  * The LEAP packet also contains the user name.
376  */
377  reply->name = talloc_array(reply, char, user_name->vp_length + 1);
378  if (!reply->name) {
379  talloc_free(reply);
380  return NULL;
381  }
382 
383  /*
384  * Copy the name over, and ensure it's NUL terminated.
385  */
386  memcpy(reply->name, user_name->vp_strvalue, user_name->vp_length);
387  reply->name[user_name->vp_length] = '\0';
388  reply->name_len = user_name->vp_length;
389 
390  return reply;
391 }
392 
393 /*
394  * compose the LEAP reply packet in the EAP reply typedata
395  */
397 {
399 
400  rad_assert(eap_round->request);
401  rad_assert(reply);
402 
403  /*
404  * We need the name and the challenge.
405  */
406  switch (reply->code) {
407  case PW_EAP_REQUEST:
408  case PW_EAP_RESPONSE:
409  eap_round->request->type.num = PW_EAP_LEAP;
410  eap_round->request->type.length = reply->length;
411 
412  eap_round->request->type.data = talloc_array(eap_round->request, uint8_t, reply->length);
413  if (!eap_round->request->type.data) {
414  return 0;
415  }
416 
417  data = (leap_packet_raw_t *) eap_round->request->type.data;
418  data->version = 0x01;
419  data->unused = 0;
420  data->count = reply->count;
421 
422  /*
423  * N bytes of the challenge, followed by the user name.
424  */
425  memcpy(&data->challenge[0], reply->challenge, reply->count);
426  memcpy(&data->challenge[reply->count], reply->name, reply->name_len);
427  break;
428 
429  /*
430  * EAP-Success packets don't contain any data
431  * other than the header.
432  */
433  case PW_EAP_SUCCESS:
434  eap_round->request->type.length = 0;
435  break;
436 
437  default:
438  REDEBUG("Internal sanity check failed");
439  return 0;
440  }
441 
442  /*
443  * Set the EAP code.
444  */
445  eap_round->request->code = reply->code;
446 
447  return 1;
448 }
uint8_t challenge[1]
Definition: eap_leap.h:35
void fr_md5_calc(uint8_t *out, uint8_t const *in, size_t inlen)
Calculate the MD5 hash of the contents of a buffer.
Definition: md5.c:28
size_t length
Definition: eap_types.h:135
uint8_t unused
Definition: eap_leap.h:33
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: radius.c:1621
uint8_t version
Definition: eap_leap.h:32
char const * secret
Secret PSK.
Definition: clients.h:43
#define EAP_HEADER_LEN
Definition: eap_types.h:35
unsigned char code
Definition: eap_leap.h:42
static int eap_leap_ntpwdhash(uint8_t *out, REQUEST *request, VALUE_PAIR *password)
Definition: eap_leap.c:161
void fr_md4_calc(uint8_t out[MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Calculate the MD4 hash of the contents of a buffer.
Definition: md4.c:24
eap_packet_t * request
Packet we will send to the peer.
Definition: eap.h:45
leap_packet_t * eap_leap_initiate(REQUEST *request, eap_round_t *eap_round, VALUE_PAIR *user_name)
Definition: eap_leap.c:346
eap_type_data_t type
Definition: eap_types.h:136
static unsigned int hash(char const *username, unsigned int tablesize)
Definition: rlm_passwd.c:124
size_t name_len
Definition: eap_leap.h:47
void fr_pair_value_strsteal(VALUE_PAIR *vp, char const *src)
Reparent an allocated char buffer to a VALUE_PAIR.
Definition: pair.c:1955
#define rad_assert(expr)
Definition: rad_assert.h:38
unsigned char * challenge
Definition: eap_leap.h:46
eap_type_t num
Definition: eap_types.h:122
uint8_t id
Definition: eap_types.h:134
size_t length
Definition: eap_leap.h:44
size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
Convert hex strings to binary data.
Definition: misc.c:220
unsigned int attr
Attribute number.
Definition: dict.h:79
Definition: token.h:43
uint8_t peer_response[24]
Definition: eap_leap.h:58
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
size_t length
Definition: eap_types.h:123
Contains a pair of request and response packets.
Definition: eap.h:43
uint8_t vector[AUTH_VECTOR_LEN]
RADIUS authentication vector.
Definition: libradius.h:157
ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
Convert UTF8 string to UCS2 encoding.
Definition: misc.c:580
int eap_leap_stage4(REQUEST *request, leap_packet_t *packet, VALUE_PAIR *password, leap_session_t *session)
Definition: eap_leap.c:207
#define LEAP_HEADER_LEN
Definition: eap_leap.h:17
#define FR_TUNNEL_PW_ENC_LENGTH(_x)
Definition: libradius.h:232
#define RDEBUG2(fmt,...)
Definition: log.h:244
uint8_t count
Definition: eap_leap.h:34
uint8_t data[]
Definition: eap_pwd.h:625
#define pair_make_reply(_a, _b, _c)
Definition: radiusd.h:546
void eap_leap_mschap(unsigned char const *win_password, unsigned char const *challenge, unsigned char *response)
Definition: smbdes.c:317
int eap_leap_compose(REQUEST *request, eap_round_t *eap_round, leap_packet_t *reply)
Definition: eap_leap.c:396
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
#define REDEBUG(fmt,...)
Definition: log.h:254
uint8_t peer_challenge[8]
Definition: eap_leap.h:57
char * name
Definition: eap_leap.h:48
leap_packet_t * eap_leap_stage6(REQUEST *request, leap_packet_t *packet, VALUE_PAIR *user_name, VALUE_PAIR *password, leap_session_t *session)
Definition: eap_leap.c:240
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
eap_packet_t * response
Packet we received from the peer.
Definition: eap.h:44
int count
Definition: eap_leap.h:45
unsigned char id
Definition: eap_leap.h:43
RADCLIENT * client
The client that originally sent us the request.
Definition: radiusd.h:219
#define RCSID(id)
Definition: build.h:135
int fr_radius_encode_tunnel_password(char *encpw, size_t *len, char const *secret, uint8_t const *vector)
Encode Tunnel-Password attributes when sending them out on the wire.
leap_packet_t * eap_leap_extract(REQUEST *request, eap_round_t *eap_round)
Definition: eap_leap.c:59
eap_code_t code
Definition: eap_types.h:133
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
Definition: pair.c:1905
uint8_t * data
Definition: eap_types.h:124