Fix some warnings triggered by gcc 4.6 -Wunused-but-set-variable
[strongswan.git] / src / libcharon / plugins / eap_mschapv2 / eap_mschapv2.c
1 /*
2 * Copyright (C) 2009 Tobias Brunner
3 * Copyright (C) 2010 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "eap_mschapv2.h"
18
19 #include <ctype.h>
20 #include <unistd.h>
21
22 #include <daemon.h>
23 #include <library.h>
24 #include <utils/enumerator.h>
25 #include <crypto/crypters/crypter.h>
26 #include <crypto/hashers/hasher.h>
27
28 typedef struct private_eap_mschapv2_t private_eap_mschapv2_t;
29
30 /**
31 * Private data of an eap_mschapv2_t object.
32 */
33 struct private_eap_mschapv2_t
34 {
35 /**
36 * Public authenticator_t interface.
37 */
38 eap_mschapv2_t public;
39
40 /**
41 * ID of the server
42 */
43 identification_t *server;
44
45 /**
46 * ID of the peer
47 */
48 identification_t *peer;
49
50 /**
51 * challenge sent by the server
52 */
53 chunk_t challenge;
54
55 /**
56 * generated NT-Response
57 */
58 chunk_t nt_response;
59
60 /**
61 * generated Authenticator Response
62 */
63 chunk_t auth_response;
64
65 /**
66 * generated MSK
67 */
68 chunk_t msk;
69
70 /**
71 * EAP message identifier
72 */
73 u_int8_t identifier;
74
75 /**
76 * MS-CHAPv2-ID (session ID, increases with each retry)
77 */
78 u_int8_t mschapv2id;
79
80 /**
81 * Number of retries
82 */
83 int retries;
84 };
85
86 /**
87 * OpCodes
88 */
89 enum mschapv2_opcode_t
90 {
91 MSCHAPV2_CHALLENGE = 1,
92 MSCHAPV2_RESPONSE = 2,
93 MSCHAPV2_SUCCESS = 3,
94 MSCHAPV2_FAILURE = 4,
95 MSCHAPV2_CHANGE_PASSWORD = 7,
96 };
97
98 /**
99 * Names for OpCodes
100 */
101 ENUM_BEGIN(mschapv2_opcode_names, MSCHAPV2_CHALLENGE, MSCHAPV2_FAILURE,
102 "CHALLENGE",
103 "RESPONSE",
104 "SUCCESS",
105 "FAILURE");
106 ENUM_NEXT(mschapv2_opcode_names, MSCHAPV2_CHANGE_PASSWORD, MSCHAPV2_CHANGE_PASSWORD, MSCHAPV2_FAILURE,
107 "CHANGE_PASSWORD");
108 ENUM_END(mschapv2_opcode_names, MSCHAPV2_CHANGE_PASSWORD);
109
110 /**
111 * Error codes
112 */
113 enum mschapv2_error_t
114 {
115 ERROR_RESTRICTED_LOGON_HOURS = 646,
116 ERROR_ACCT_DISABLED = 647,
117 ERROR_PASSWD_EXPIRED = 648,
118 ERROR_NO_DIALIN_PERMISSION = 649,
119 ERROR_AUTHENTICATION_FAILURE = 691,
120 ERROR_CHANGING_PASSWORD = 709,
121 };
122
123 /**
124 * Names for error codes
125 */
126 ENUM_BEGIN(mschapv2_error_names, ERROR_RESTRICTED_LOGON_HOURS, ERROR_NO_DIALIN_PERMISSION,
127 "ERROR_RESTRICTED_LOGON_HOURS",
128 "ERROR_ACCT_DISABLED",
129 "ERROR_PASSWD_EXPIRED",
130 "ERROR_NO_DIALIN_PERMISSION");
131 ENUM_NEXT(mschapv2_error_names, ERROR_AUTHENTICATION_FAILURE, ERROR_AUTHENTICATION_FAILURE, ERROR_NO_DIALIN_PERMISSION,
132 "ERROR_AUTHENTICATION_FAILURE");
133 ENUM_NEXT(mschapv2_error_names, ERROR_CHANGING_PASSWORD, ERROR_CHANGING_PASSWORD, ERROR_AUTHENTICATION_FAILURE,
134 "ERROR_CHANGING_PASSWORD");
135 ENUM_END(mschapv2_error_names, ERROR_CHANGING_PASSWORD);
136
137 /* Length of the challenge */
138 #define CHALLENGE_LEN 16
139 /* Length of the response (see eap_mschapv2_response_t) */
140 #define RESPONSE_LEN 49
141 /* Length of the authenticator response string ("S=<...>") */
142 #define AUTH_RESPONSE_LEN 42
143 /* Name we send as authenticator */
144 #define MSCHAPV2_HOST_NAME "strongSwan"
145 /* Message sent on success */
146 #define SUCCESS_MESSAGE " M=Welcome2strongSwan"
147 /* Message sent on failure */
148 #define FAILURE_MESSAGE "E=691 R=1 C="
149 /* Length of the complete failure message */
150 #define FAILURE_MESSAGE_LEN (sizeof(FAILURE_MESSAGE) + CHALLENGE_LEN * 2)
151
152 /* Number of seconds to delay retries */
153 #define RETRY_DELAY 2
154 /* Maximum number of retries */
155 #define MAX_RETRIES 2
156
157 typedef struct eap_mschapv2_header_t eap_mschapv2_header_t;
158 typedef struct eap_mschapv2_challenge_t eap_mschapv2_challenge_t;
159 typedef struct eap_mschapv2_response_t eap_mschapv2_response_t;
160
161 /**
162 * packed EAP-MS-CHAPv2 header struct
163 */
164 struct eap_mschapv2_header_t
165 {
166 /** EAP code (REQUEST/RESPONSE) */
167 u_int8_t code;
168 /** unique message identifier */
169 u_int8_t identifier;
170 /** length of whole message */
171 u_int16_t length;
172 /** EAP type */
173 u_int8_t type;
174 /** MS-CHAPv2 OpCode */
175 u_int8_t opcode;
176 /** MS-CHAPv2-ID (equals identifier) */
177 u_int8_t ms_chapv2_id;
178 /** MS-Length (defined as length - 5) */
179 u_int16_t ms_length;
180 /** packet data (determined by OpCode) */
181 u_int8_t data[];
182 }__attribute__((__packed__));
183
184 /**
185 * packed data for a MS-CHAPv2 Challenge packet
186 */
187 struct eap_mschapv2_challenge_t
188 {
189 /** Value-Size */
190 u_int8_t value_size;
191 /** Challenge */
192 u_int8_t challenge[CHALLENGE_LEN];
193 /** Name */
194 u_int8_t name[];
195 }__attribute__((__packed__));
196
197 /**
198 * packed data for a MS-CHAPv2 Response packet
199 */
200 struct eap_mschapv2_response_t
201 {
202 /** Value-Size */
203 u_int8_t value_size;
204 /** Response */
205 struct
206 {
207 /* Peer-Challenge*/
208 u_int8_t peer_challenge[CHALLENGE_LEN];
209 /* Reserved (=zero) */
210 u_int8_t peer_reserved[8];
211 /* NT-Response */
212 u_int8_t nt_response[24];
213 /* Flags (=zero) */
214 u_int8_t flags;
215 } response;
216 /** Name */
217 u_int8_t name[];
218 }__attribute__((__packed__));
219
220 /**
221 * Length of the MS-CHAPv2 header
222 */
223 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
224
225 /**
226 * Length of the header for MS-CHAPv2 success/failure packets (does not include
227 * MS-CHAPv2-ID and MS-Length, i.e. 3 octets)
228 */
229 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
230
231 /**
232 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
233 * at least one octet)
234 */
235 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
236
237 /**
238 * The minimum length of an MS-CHAPv2 Response packet
239 */
240 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
241
242
243 /**
244 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
245 */
246 static chunk_t ExpandDESKey(chunk_t key)
247 {
248 static const u_char bitmask[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
249 int i;
250 u_char carry = 0;
251 chunk_t expanded;
252
253 /* expand the 7 octets to 8 octets */
254 expanded = chunk_alloc(8);
255 for (i = 0; i < 7; i++)
256 {
257 expanded.ptr[i] = ((key.ptr[i] & bitmask[i]) >> i) | (carry << (8 - i));
258 carry = key.ptr[i] & ~bitmask[i];
259 }
260 expanded.ptr[7] = carry << 1;
261
262 /* add parity bits to each octet */
263 for (i = 0; i < 8; i++)
264 {
265 u_char val = expanded.ptr[i];
266 val = (val ^ (val >> 4)) & 0x0f;
267 expanded.ptr[i] |= (0x9669 >> val) & 1;
268 }
269 return expanded;
270 }
271
272 /**
273 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
274 */
275 static status_t NtPasswordHash(chunk_t password, chunk_t *password_hash)
276 {
277 hasher_t *hasher;
278 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD4);
279 if (hasher == NULL)
280 {
281 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
282 return FAILED;
283 }
284 hasher->allocate_hash(hasher, password, password_hash);
285 hasher->destroy(hasher);
286 return SUCCESS;
287 }
288
289 /**
290 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
291 * username (without domain part)] with SHA1)
292 */
293 static status_t ChallengeHash(chunk_t peer_challenge, chunk_t server_challenge,
294 chunk_t username, chunk_t *challenge_hash)
295 {
296 chunk_t concat;
297 hasher_t *hasher;
298 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
299 if (hasher == NULL)
300 {
301 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
302 return FAILED;
303 }
304 concat = chunk_cata("ccc", peer_challenge, server_challenge, username);
305 hasher->allocate_hash(hasher, concat, challenge_hash);
306 hasher->destroy(hasher);
307 /* we need only the first 8 octets */
308 challenge_hash->len = 8;
309 return SUCCESS;
310 }
311
312 /**
313 * Calculate the challenge response (i.e. expand password_hash to three DES keys
314 * and then encrypt the 8-octet challenge_hash with these keys and concatenate
315 * the results).
316 */
317 static status_t ChallengeResponse(chunk_t challenge_hash, chunk_t password_hash,
318 chunk_t *response)
319 {
320 int i;
321 crypter_t *crypter;
322 chunk_t keys[3], z_password_hash;
323 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_DES_ECB, 8);
324 if (crypter == NULL)
325 {
326 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
327 return FAILED;
328 }
329 /* prepare keys: first pad password_hash to 21 octets, these get then split
330 * into 7-octet chunks, which then get expanded into 8-octet DES keys */
331 z_password_hash = chunk_alloca(21);
332 memset(z_password_hash.ptr, 0, z_password_hash.len);
333 memcpy(z_password_hash.ptr, password_hash.ptr, password_hash.len);
334 chunk_split(z_password_hash, "mmm", 7, &keys[0], 7, &keys[1], 7, &keys[2]);
335
336 *response = chunk_alloc(24);
337 for (i = 0; i < 3; i++)
338 {
339 chunk_t expanded, encrypted;
340 expanded = ExpandDESKey(keys[i]);
341 crypter->set_key(crypter, expanded);
342 crypter->encrypt(crypter, challenge_hash, chunk_empty, &encrypted);
343 memcpy(&response->ptr[i * 8], encrypted.ptr, encrypted.len);
344 chunk_clear(&encrypted);
345 chunk_clear(&expanded);
346 }
347 crypter->destroy(crypter);
348 return SUCCESS;
349 }
350
351 /**
352 * Computes the authenticator response
353 */
354 static status_t AuthenticatorResponse(chunk_t password_hash_hash,
355 chunk_t challenge_hash, chunk_t nt_response, chunk_t *response)
356 {
357 chunk_t magic1 = chunk_from_chars(
358 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
359 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
360 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
361 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
362 chunk_t magic2 = chunk_from_chars(
363 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
364 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
365 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
366 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
367 0x6E);
368 chunk_t digest = chunk_empty, concat;
369 hasher_t *hasher;
370
371 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
372 if (hasher == NULL)
373 {
374 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
375 return FAILED;
376 }
377
378 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
379 hasher->allocate_hash(hasher, concat, &digest);
380 concat = chunk_cata("ccc", digest, challenge_hash, magic2);
381 hasher->allocate_hash(hasher, concat, response);
382
383 hasher->destroy(hasher);
384 chunk_free(&digest);
385 return SUCCESS;
386 }
387
388 /**
389 * Generate the master session key according to RFC3079
390 */
391 static status_t GenerateMSK(chunk_t password_hash_hash,
392 chunk_t nt_response, chunk_t *msk)
393 {
394 chunk_t magic1 = chunk_from_chars(
395 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
396 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
397 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
398 chunk_t magic2 = chunk_from_chars(
399 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
400 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
401 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
402 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
403 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
404 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
405 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
406 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
407 0x6b, 0x65, 0x79, 0x2e);
408 chunk_t magic3 = chunk_from_chars(
409 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
410 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
411 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
412 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
413 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
414 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
415 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
416 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
417 0x6b, 0x65, 0x79, 0x2e);
418 chunk_t shapad1 = chunk_from_chars(
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
423 chunk_t shapad2 = chunk_from_chars(
424 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
425 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
426 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
427 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);
428 chunk_t keypad = chunk_from_chars(
429 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
431 chunk_t concat, master_key, master_receive_key, master_send_key;
432 hasher_t *hasher;
433
434 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
435 if (hasher == NULL)
436 {
437 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
438 return FAILED;
439 }
440
441 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
442 hasher->allocate_hash(hasher, concat, &master_key);
443 master_key.len = 16;
444
445 concat = chunk_cata("cccc", master_key, shapad1, magic2, shapad2);
446 hasher->allocate_hash(hasher, concat, &master_receive_key);
447 master_receive_key.len = 16;
448
449 concat = chunk_cata("cccc", master_key, shapad1, magic3, shapad2);
450 hasher->allocate_hash(hasher, concat, &master_send_key);
451 master_send_key.len = 16;
452
453 *msk = chunk_cat("cccc", master_receive_key, master_send_key, keypad, keypad);
454
455 hasher->destroy(hasher);
456 chunk_free(&master_key);
457 chunk_free(&master_receive_key);
458 chunk_free(&master_send_key);
459 return SUCCESS;
460 }
461
462 static status_t GenerateStuff(private_eap_mschapv2_t *this,
463 chunk_t server_challenge, chunk_t peer_challenge,
464 chunk_t username, chunk_t nt_hash)
465 {
466 status_t status = FAILED;
467 chunk_t nt_hash_hash = chunk_empty, challenge_hash = chunk_empty;
468
469 if (NtPasswordHash(nt_hash, &nt_hash_hash) != SUCCESS)
470 {
471 goto error;
472 }
473 if (ChallengeHash(peer_challenge, server_challenge, username,
474 &challenge_hash) != SUCCESS)
475 {
476 goto error;
477 }
478 if (ChallengeResponse(challenge_hash, nt_hash,
479 &this->nt_response) != SUCCESS)
480 {
481 goto error;
482 }
483 if (AuthenticatorResponse(nt_hash_hash, challenge_hash,
484 this->nt_response, &this->auth_response) != SUCCESS)
485 {
486 goto error;
487 }
488 if (GenerateMSK(nt_hash_hash, this->nt_response, &this->msk) != SUCCESS)
489 {
490 goto error;
491 }
492
493 status = SUCCESS;
494
495 error:
496 chunk_free(&nt_hash_hash);
497 chunk_free(&challenge_hash);
498 return status;
499 }
500
501 /**
502 * Converts an ASCII string into a UTF-16 (little-endian) string
503 */
504 static chunk_t ascii_to_unicode(chunk_t ascii)
505 {
506 int i;
507 chunk_t unicode = chunk_alloc(ascii.len * 2);
508 for (i = 0; i < ascii.len; i++)
509 {
510 unicode.ptr[i * 2] = ascii.ptr[i];
511 unicode.ptr[i * 2 + 1] = 0;
512 }
513 return unicode;
514 }
515
516 /**
517 * sanitize a string for printing
518 */
519 static char* sanitize(char *str)
520 {
521 char *pos = str;
522
523 while (pos && *pos)
524 {
525 if (!isprint(*pos))
526 {
527 *pos = '?';
528 }
529 pos++;
530 }
531 return str;
532 }
533
534 /**
535 * Returns a chunk of just the username part of the given user identity.
536 * Note: the chunk points to internal data of the identification.
537 */
538 static chunk_t extract_username(identification_t* identification)
539 {
540 char *has_domain;
541 chunk_t id;
542 id = identification->get_encoding(identification);
543 has_domain = (char*)memchr(id.ptr, '\\', id.len);
544 if (has_domain)
545 {
546 int len;
547 has_domain++; /* skip the backslash */
548 len = id.len - ((u_char*)has_domain - id.ptr);
549 return len > 0 ? chunk_create(has_domain, len) : chunk_empty;
550 }
551 return id;
552 }
553
554 /**
555 * Set the ms_length field using aligned write
556 */
557 static void set_ms_length(eap_mschapv2_header_t *eap, u_int16_t len)
558 {
559 len = htons(len - 5);
560 memcpy(&eap->ms_length, &len, sizeof(u_int16_t));
561 }
562
563 METHOD(eap_method_t, initiate_peer, status_t,
564 private_eap_mschapv2_t *this, eap_payload_t **out)
565 {
566 /* peer never initiates */
567 return FAILED;
568 }
569
570 METHOD(eap_method_t, initiate_server, status_t,
571 private_eap_mschapv2_t *this, eap_payload_t **out)
572 {
573 rng_t *rng;
574 eap_mschapv2_header_t *eap;
575 eap_mschapv2_challenge_t *cha;
576 const char *name = MSCHAPV2_HOST_NAME;
577 u_int16_t len = CHALLENGE_PAYLOAD_LEN + sizeof(MSCHAPV2_HOST_NAME) - 1;
578
579 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
580 if (!rng)
581 {
582 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no RNG");
583 return FAILED;
584 }
585 rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge);
586 rng->destroy(rng);
587
588 eap = alloca(len);
589 eap->code = EAP_REQUEST;
590 eap->identifier = this->identifier;
591 eap->length = htons(len);
592 eap->type = EAP_MSCHAPV2;
593 eap->opcode = MSCHAPV2_CHALLENGE;
594 eap->ms_chapv2_id = this->mschapv2id;
595 set_ms_length(eap, len);
596
597 cha = (eap_mschapv2_challenge_t*)eap->data;
598 cha->value_size = CHALLENGE_LEN;
599 memcpy(cha->challenge, this->challenge.ptr, this->challenge.len);
600 memcpy(cha->name, name, sizeof(MSCHAPV2_HOST_NAME) - 1);
601
602 *out = eap_payload_create_data(chunk_create((void*) eap, len));
603 return NEED_MORE;
604 }
605
606 static bool get_nt_hash(private_eap_mschapv2_t *this, identification_t *me,
607 identification_t *other, chunk_t *nt_hash)
608 {
609 shared_key_t *shared;
610 chunk_t password;
611
612 /* try to find a stored NT_HASH first */
613 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_NT_HASH, me, other);
614 if (shared )
615 {
616 *nt_hash = chunk_clone(shared->get_key(shared));
617 shared->destroy(shared);
618 return TRUE;
619 }
620
621 /* fallback to plaintext password */
622 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, me, other);
623 if (shared)
624 {
625 password = ascii_to_unicode(shared->get_key(shared));
626 shared->destroy(shared);
627
628 if (NtPasswordHash(password, nt_hash) == SUCCESS)
629 {
630 chunk_clear(&password);
631 return TRUE;
632 }
633 chunk_clear(&password);
634 }
635 return FALSE;
636 }
637
638 /**
639 * Process MS-CHAPv2 Challenge Requests
640 */
641 static status_t process_peer_challenge(private_eap_mschapv2_t *this,
642 eap_payload_t *in, eap_payload_t **out)
643 {
644 rng_t *rng;
645 eap_mschapv2_header_t *eap;
646 eap_mschapv2_challenge_t *cha;
647 eap_mschapv2_response_t *res;
648 chunk_t data, peer_challenge, username, nt_hash;
649 u_int16_t len = RESPONSE_PAYLOAD_LEN;
650
651 data = in->get_data(in);
652 eap = (eap_mschapv2_header_t*)data.ptr;
653
654 /* the name MUST be at least one octet long */
655 if (data.len < CHALLENGE_PAYLOAD_LEN + 1)
656 {
657 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
658 return FAILED;
659 }
660
661 cha = (eap_mschapv2_challenge_t*)eap->data;
662
663 if (cha->value_size != CHALLENGE_LEN)
664 {
665 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
666 "invalid challenge size");
667 return FAILED;
668 }
669
670 this->mschapv2id = eap->ms_chapv2_id;
671 this->challenge = chunk_clone(chunk_create(cha->challenge, CHALLENGE_LEN));
672
673 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
674 if (!rng)
675 {
676 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no RNG");
677 return FAILED;
678 }
679 peer_challenge = chunk_alloca(CHALLENGE_LEN);
680 rng->get_bytes(rng, CHALLENGE_LEN, peer_challenge.ptr);
681 rng->destroy(rng);
682
683 if (!get_nt_hash(this, this->peer, this->server, &nt_hash))
684 {
685 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
686 this->server, this->peer);
687 return NOT_FOUND;
688 }
689
690 username = extract_username(this->peer);
691 len += username.len;
692
693 if (GenerateStuff(this, this->challenge, peer_challenge,
694 username, nt_hash) != SUCCESS)
695 {
696 DBG1(DBG_IKE, "EAP-MS-CHAPv2 generating NT-Response failed");
697 chunk_clear(&nt_hash);
698 return FAILED;
699 }
700 chunk_clear(&nt_hash);
701
702 eap = alloca(len);
703 eap->code = EAP_RESPONSE;
704 eap->identifier = this->identifier;
705 eap->length = htons(len);
706 eap->type = EAP_MSCHAPV2;
707 eap->opcode = MSCHAPV2_RESPONSE;
708 eap->ms_chapv2_id = this->mschapv2id;
709 set_ms_length(eap, len);
710
711 res = (eap_mschapv2_response_t*)eap->data;
712 res->value_size = RESPONSE_LEN;
713 memset(&res->response, 0, RESPONSE_LEN);
714 memcpy(res->response.peer_challenge, peer_challenge.ptr, peer_challenge.len);
715 memcpy(res->response.nt_response, this->nt_response.ptr, this->nt_response.len);
716
717 username = this->peer->get_encoding(this->peer);
718 memcpy(res->name, username.ptr, username.len);
719
720 *out = eap_payload_create_data(chunk_create((void*) eap, len));
721 return NEED_MORE;
722 }
723
724 /**
725 * Process MS-CHAPv2 Success Requests
726 */
727 static status_t process_peer_success(private_eap_mschapv2_t *this,
728 eap_payload_t *in, eap_payload_t **out)
729 {
730 status_t status = FAILED;
731 enumerator_t *enumerator;
732 eap_mschapv2_header_t *eap;
733 chunk_t data, auth_string = chunk_empty;
734 char *message, *token, *msg = NULL;
735 int message_len;
736 u_int16_t len = SHORT_HEADER_LEN;
737
738 data = in->get_data(in);
739 eap = (eap_mschapv2_header_t*)data.ptr;
740
741 if (data.len < AUTH_RESPONSE_LEN)
742 {
743 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
744 return FAILED;
745 }
746
747 message_len = data.len - HEADER_LEN;
748 message = malloc(message_len + 1);
749 memcpy(message, eap->data, message_len);
750 message[message_len] = '\0';
751
752 /* S=<auth_string> M=<msg> */
753 enumerator = enumerator_create_token(message, " ", " ");
754 while (enumerator->enumerate(enumerator, &token))
755 {
756 if (strneq(token, "S=", 2))
757 {
758 chunk_t hex;
759 token += 2;
760 if (strlen(token) != AUTH_RESPONSE_LEN - 2)
761 {
762 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
763 "invalid auth string");
764 goto error;
765 }
766 hex = chunk_create(token, AUTH_RESPONSE_LEN - 2);
767 auth_string = chunk_from_hex(hex, NULL);
768 }
769 else if (strneq(token, "M=", 2))
770 {
771 token += 2;
772 msg = strdup(token);
773 }
774 }
775 enumerator->destroy(enumerator);
776
777 if (auth_string.ptr == NULL)
778 {
779 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
780 "auth string missing");
781 goto error;
782 }
783
784 if (!chunk_equals(this->auth_response, auth_string))
785 {
786 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
787 goto error;
788 }
789
790 DBG1(DBG_IKE, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg));
791
792 eap = alloca(len);
793 eap->code = EAP_RESPONSE;
794 eap->identifier = this->identifier;
795 eap->length = htons(len);
796 eap->type = EAP_MSCHAPV2;
797 eap->opcode = MSCHAPV2_SUCCESS;
798
799 *out = eap_payload_create_data(chunk_create((void*) eap, len));
800 status = NEED_MORE;
801
802 error:
803 chunk_free(&auth_string);
804 free(message);
805 free(msg);
806 return status;
807 }
808
809 static status_t process_peer_failure(private_eap_mschapv2_t *this,
810 eap_payload_t *in, eap_payload_t **out)
811 {
812 status_t status = FAILED;
813 enumerator_t *enumerator;
814 eap_mschapv2_header_t *eap;
815 chunk_t data;
816 char *message, *token, *msg = NULL;
817 int message_len, error = 0;
818 chunk_t challenge = chunk_empty;
819
820 data = in->get_data(in);
821 eap = (eap_mschapv2_header_t*)data.ptr;
822
823 if (data.len < 3) /* we want at least an error code: E=e */
824 {
825 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
826 return FAILED;
827 }
828
829 message_len = data.len - HEADER_LEN;
830 message = malloc(message_len + 1);
831 memcpy(message, eap->data, message_len);
832 message[message_len] = '\0';
833
834 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
835 enumerator = enumerator_create_token(message, " ", " ");
836 while (enumerator->enumerate(enumerator, &token))
837 {
838 if (strneq(token, "E=", 2))
839 {
840 token += 2;
841 error = atoi(token);
842 }
843 else if (strneq(token, "R=", 2))
844 {
845 /* ignore retriable */
846 }
847 else if (strneq(token, "C=", 2))
848 {
849 chunk_t hex;
850 token += 2;
851 if (strlen(token) != 2 * CHALLENGE_LEN)
852 {
853 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message:"
854 "invalid challenge");
855 goto error;
856 }
857 hex = chunk_create(token, 2 * CHALLENGE_LEN);
858 challenge = chunk_from_hex(hex, NULL);
859 }
860 else if (strneq(token, "V=", 2))
861 {
862 /* ignore version */
863 }
864 else if (strneq(token, "M=", 2))
865 {
866 token += 2;
867 msg = strdup(token);
868 }
869 }
870 enumerator->destroy(enumerator);
871
872 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed with error %N: '%s'",
873 mschapv2_error_names, error, sanitize(msg));
874
875 /**
876 * at this point, if the error is retriable, we MAY retry the authentication
877 * or MAY send a Change Password packet.
878 *
879 * if the error is not retriable (or if we do neither of the above), we
880 * SHOULD send a Failure Response packet.
881 * windows clients don't do that, and since windows server 2008 r2 behaves
882 * pretty odd if we do send a Failure Response, we just don't send one
883 * either. windows 7 actually sends a delete notify (which, according to the
884 * logs, results in an error on windows server 2008 r2).
885 *
886 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
887 * a disabled account but returns the windows error code in a notify payload
888 * of type 12345.
889 */
890
891 status = FAILED;
892
893 error:
894 chunk_free(&challenge);
895 free(message);
896 free(msg);
897 return status;
898 }
899
900 METHOD(eap_method_t, process_peer, status_t,
901 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
902 {
903 chunk_t data;
904 eap_mschapv2_header_t *eap;
905
906 this->identifier = in->get_identifier(in);
907 data = in->get_data(in);
908 if (data.len < SHORT_HEADER_LEN)
909 {
910 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message");
911 return FAILED;
912 }
913
914 eap = (eap_mschapv2_header_t*)data.ptr;
915
916 switch (eap->opcode)
917 {
918 case MSCHAPV2_CHALLENGE:
919 {
920 return process_peer_challenge(this, in, out);
921 }
922 case MSCHAPV2_SUCCESS:
923 {
924 return process_peer_success(this, in, out);
925 }
926 case MSCHAPV2_FAILURE:
927 {
928 return process_peer_failure(this, in, out);
929 }
930 default:
931 {
932 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
933 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
934 break;
935 }
936 }
937 return FAILED;
938 }
939
940 /**
941 * Handles retries on the server
942 */
943 static status_t process_server_retry(private_eap_mschapv2_t *this,
944 eap_payload_t **out)
945 {
946 eap_mschapv2_header_t *eap;
947 rng_t *rng;
948 chunk_t hex;
949 char msg[FAILURE_MESSAGE_LEN];
950 u_int16_t len = HEADER_LEN + FAILURE_MESSAGE_LEN - 1; /* no null byte */
951
952 if (++this->retries > MAX_RETRIES)
953 {
954 /* we MAY send a Failure Request with R=0, but windows 7 does not
955 * really like that and does not respond with a Failure Response.
956 * so, to clean up our state we just fail with an EAP-Failure.
957 * this gives an unknown error on the windows side, but is also fine
958 * with the standard. */
959 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed: "
960 "maximum number of retries reached");
961 return FAILED;
962 }
963
964 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries);
965
966 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
967 if (!rng)
968 {
969 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no RNG");
970 return FAILED;
971 }
972 rng->get_bytes(rng, CHALLENGE_LEN, this->challenge.ptr);
973 rng->destroy(rng);
974
975 chunk_free(&this->nt_response);
976 chunk_free(&this->auth_response);
977 chunk_free(&this->msk);
978
979 eap = alloca(len);
980 eap->code = EAP_REQUEST;
981 eap->identifier = ++this->identifier;
982 eap->length = htons(len);
983 eap->type = EAP_MSCHAPV2;
984 eap->opcode = MSCHAPV2_FAILURE;
985 eap->ms_chapv2_id = this->mschapv2id++; /* increase for each retry */
986 set_ms_length(eap, len);
987
988 hex = chunk_to_hex(this->challenge, NULL, TRUE);
989 snprintf(msg, FAILURE_MESSAGE_LEN, "%s%s", FAILURE_MESSAGE, hex.ptr);
990 chunk_free(&hex);
991 memcpy(eap->data, msg, FAILURE_MESSAGE_LEN - 1); /* no null byte */
992 *out = eap_payload_create_data(chunk_create((void*) eap, len));
993
994 /* delay the response for some time to make brute-force attacks harder */
995 sleep(RETRY_DELAY);
996
997 return NEED_MORE;
998 }
999
1000 /**
1001 * Process MS-CHAPv2 Response response packets
1002 */
1003 static status_t process_server_response(private_eap_mschapv2_t *this,
1004 eap_payload_t *in, eap_payload_t **out)
1005 {
1006 eap_mschapv2_header_t *eap;
1007 eap_mschapv2_response_t *res;
1008 chunk_t data, peer_challenge, username, nt_hash;
1009 identification_t *userid;
1010 int name_len;
1011 char buf[256];
1012
1013 data = in->get_data(in);
1014 eap = (eap_mschapv2_header_t*)data.ptr;
1015
1016 if (data.len < RESPONSE_PAYLOAD_LEN)
1017 {
1018 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1019 return FAILED;
1020 }
1021
1022 res = (eap_mschapv2_response_t*)eap->data;
1023 peer_challenge = chunk_create(res->response.peer_challenge, CHALLENGE_LEN);
1024
1025 name_len = min(data.len - RESPONSE_PAYLOAD_LEN, 255);
1026 snprintf(buf, sizeof(buf), "%.*s", name_len, res->name);
1027 userid = identification_create_from_string(buf);
1028 DBG2(DBG_IKE, "EAP-MS-CHAPv2 username: '%Y'", userid);
1029 username = extract_username(userid);
1030
1031 if (!get_nt_hash(this, this->server, userid, &nt_hash))
1032 {
1033 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
1034 this->server, userid);
1035 /* FIXME: windows 7 always sends the username that is first entered in
1036 * the username box, even, if the user changes it during retries (probably
1037 * to keep consistent with the EAP-Identity).
1038 * thus, we could actually fail here, because retries do not make much
1039 * sense. on the other hand, an attacker could guess usernames, if the
1040 * error messages were different. */
1041 userid->destroy(userid);
1042 return process_server_retry(this, out);
1043 }
1044
1045 if (GenerateStuff(this, this->challenge, peer_challenge,
1046 username, nt_hash) != SUCCESS)
1047 {
1048 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
1049 userid->destroy(userid);
1050 chunk_clear(&nt_hash);
1051 return FAILED;
1052 }
1053 userid->destroy(userid);
1054 chunk_clear(&nt_hash);
1055
1056 if (memeq(res->response.nt_response, this->nt_response.ptr,
1057 this->nt_response.len))
1058 {
1059 chunk_t hex;
1060 char msg[AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE)];
1061 u_int16_t len = HEADER_LEN + AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE);
1062
1063 eap = alloca(len);
1064 eap->code = EAP_REQUEST;
1065 eap->identifier = ++this->identifier;
1066 eap->length = htons(len);
1067 eap->type = EAP_MSCHAPV2;
1068 eap->opcode = MSCHAPV2_SUCCESS;
1069 eap->ms_chapv2_id = this->mschapv2id;
1070 set_ms_length(eap, len);
1071
1072 hex = chunk_to_hex(this->auth_response, NULL, TRUE);
1073 snprintf(msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE),
1074 "S=%s%s", hex.ptr, SUCCESS_MESSAGE);
1075 chunk_free(&hex);
1076 memcpy(eap->data, msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE));
1077 *out = eap_payload_create_data(chunk_create((void*) eap, len));
1078 return NEED_MORE;
1079 }
1080
1081 return process_server_retry(this, out);
1082 }
1083
1084 METHOD(eap_method_t, process_server, status_t,
1085 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
1086 {
1087 eap_mschapv2_header_t *eap;
1088 chunk_t data;
1089
1090 if (this->identifier != in->get_identifier(in))
1091 {
1092 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
1093 "unexpected identifier");
1094 return FAILED;
1095 }
1096
1097 data = in->get_data(in);
1098 if (data.len < SHORT_HEADER_LEN)
1099 {
1100 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1101 return FAILED;
1102 }
1103
1104 eap = (eap_mschapv2_header_t*)data.ptr;
1105
1106 switch (eap->opcode)
1107 {
1108 case MSCHAPV2_RESPONSE:
1109 {
1110 return process_server_response(this, in, out);
1111 }
1112 case MSCHAPV2_SUCCESS:
1113 {
1114 return SUCCESS;
1115 }
1116 case MSCHAPV2_FAILURE:
1117 {
1118 return FAILED;
1119 }
1120 default:
1121 {
1122 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
1123 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1124 break;
1125 }
1126 }
1127 return FAILED;
1128 }
1129
1130 METHOD(eap_method_t, get_type, eap_type_t,
1131 private_eap_mschapv2_t *this, u_int32_t *vendor)
1132 {
1133 *vendor = 0;
1134 return EAP_MSCHAPV2;
1135 }
1136
1137 METHOD(eap_method_t, get_msk, status_t,
1138 private_eap_mschapv2_t *this, chunk_t *msk)
1139 {
1140 if (this->msk.ptr)
1141 {
1142 *msk = this->msk;
1143 return SUCCESS;
1144 }
1145 return FAILED;
1146 }
1147
1148 METHOD(eap_method_t, get_identifier, u_int8_t,
1149 private_eap_mschapv2_t *this)
1150 {
1151 return this->identifier;
1152 }
1153
1154 METHOD(eap_method_t, set_identifier, void,
1155 private_eap_mschapv2_t *this, u_int8_t identifier)
1156 {
1157 this->identifier = identifier;
1158 }
1159
1160 METHOD(eap_method_t, is_mutual, bool,
1161 private_eap_mschapv2_t *this)
1162 {
1163 return FALSE;
1164 }
1165
1166 METHOD(eap_method_t, destroy, void,
1167 private_eap_mschapv2_t *this)
1168 {
1169 this->peer->destroy(this->peer);
1170 this->server->destroy(this->server);
1171 chunk_free(&this->challenge);
1172 chunk_free(&this->nt_response);
1173 chunk_free(&this->auth_response);
1174 chunk_free(&this->msk);
1175 free(this);
1176 }
1177
1178 /**
1179 * Generic constructor
1180 */
1181 static private_eap_mschapv2_t *eap_mschapv2_create_generic(identification_t *server, identification_t *peer)
1182 {
1183 private_eap_mschapv2_t *this;
1184
1185 INIT(this,
1186 .public = {
1187 .eap_method_interface = {
1188 .get_type = _get_type,
1189 .is_mutual = _is_mutual,
1190 .get_msk = _get_msk,
1191 .get_identifier = _get_identifier,
1192 .set_identifier = _set_identifier,
1193 .destroy = _destroy,
1194 },
1195 },
1196 .peer = peer->clone(peer),
1197 .server = server->clone(server),
1198 );
1199
1200 return this;
1201 }
1202
1203 /*
1204 * see header
1205 */
1206 eap_mschapv2_t *eap_mschapv2_create_server(identification_t *server, identification_t *peer)
1207 {
1208 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1209
1210 this->public.eap_method_interface.initiate = _initiate_server;
1211 this->public.eap_method_interface.process = _process_server;
1212
1213 /* generate a non-zero identifier */
1214 do
1215 {
1216 this->identifier = random();
1217 } while (!this->identifier);
1218
1219 this->mschapv2id = this->identifier;
1220
1221 return &this->public;
1222 }
1223
1224 /*
1225 * see header
1226 */
1227 eap_mschapv2_t *eap_mschapv2_create_peer(identification_t *server, identification_t *peer)
1228 {
1229 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1230
1231 this->public.eap_method_interface.initiate = _initiate_peer;
1232 this->public.eap_method_interface.process = _process_peer;
1233
1234 return &this->public;
1235 }
1236