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