2 * Copyright (C) 2009 Tobias Brunner
3 * Copyright (C) 2010 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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>.
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
17 #include "eap_mschapv2.h"
24 #include <collections/enumerator.h>
25 #include <crypto/crypters/crypter.h>
26 #include <crypto/hashers/hasher.h>
28 typedef struct private_eap_mschapv2_t private_eap_mschapv2_t
;
31 * Private data of an eap_mschapv2_t object.
33 struct private_eap_mschapv2_t
36 * Public authenticator_t interface.
38 eap_mschapv2_t
public;
43 identification_t
*server
;
48 identification_t
*peer
;
51 * challenge sent by the server
56 * generated NT-Response
61 * generated Authenticator Response
63 chunk_t auth_response
;
71 * EAP message identifier
76 * MS-CHAPv2-ID (session ID, increases with each retry)
89 enum mschapv2_opcode_t
91 MSCHAPV2_CHALLENGE
= 1,
92 MSCHAPV2_RESPONSE
= 2,
95 MSCHAPV2_CHANGE_PASSWORD
= 7,
101 ENUM_BEGIN(mschapv2_opcode_names
, MSCHAPV2_CHALLENGE
, MSCHAPV2_FAILURE
,
106 ENUM_NEXT(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_FAILURE
,
108 ENUM_END(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
);
113 enum mschapv2_error_t
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,
124 * Names for error codes
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
);
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)
152 /* Number of seconds to delay retries */
153 #define RETRY_DELAY 2
154 /* Maximum number of retries */
155 #define MAX_RETRIES 2
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
;
162 * packed EAP-MS-CHAPv2 header struct
164 struct eap_mschapv2_header_t
166 /** EAP code (REQUEST/RESPONSE) */
168 /** unique message identifier */
170 /** length of whole message */
174 /** MS-CHAPv2 OpCode */
176 /** MS-CHAPv2-ID (equals identifier) */
177 u_int8_t ms_chapv2_id
;
178 /** MS-Length (defined as length - 5) */
180 /** packet data (determined by OpCode) */
182 }__attribute__((__packed__
));
185 * packed data for a MS-CHAPv2 Challenge packet
187 struct eap_mschapv2_challenge_t
192 u_int8_t challenge
[CHALLENGE_LEN
];
195 }__attribute__((__packed__
));
198 * packed data for a MS-CHAPv2 Response packet
200 struct eap_mschapv2_response_t
208 u_int8_t peer_challenge
[CHALLENGE_LEN
];
209 /* Reserved (=zero) */
210 u_int8_t peer_reserved
[8];
212 u_int8_t nt_response
[24];
218 }__attribute__((__packed__
));
221 * Length of the MS-CHAPv2 header
223 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
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)
229 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
232 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
233 * at least one octet)
235 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
238 * The minimum length of an MS-CHAPv2 Response packet
240 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
244 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
246 static chunk_t
ExpandDESKey(chunk_t key
)
248 static const u_char bitmask
[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
253 /* expand the 7 octets to 8 octets */
254 expanded
= chunk_alloc(8);
255 for (i
= 0; i
< 7; i
++)
257 expanded
.ptr
[i
] = ((key
.ptr
[i
] & bitmask
[i
]) >> i
) | (carry
<< (8 - i
));
258 carry
= key
.ptr
[i
] & ~bitmask
[i
];
260 expanded
.ptr
[7] = carry
<< 1;
262 /* add parity bits to each octet */
263 for (i
= 0; i
< 8; i
++)
265 u_char val
= expanded
.ptr
[i
];
266 val
= (val
^ (val
>> 4)) & 0x0f;
267 expanded
.ptr
[i
] |= (0x9669 >> val
) & 1;
273 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
275 static status_t
NtPasswordHash(chunk_t password
, chunk_t
*password_hash
)
278 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD4
);
281 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
284 if (!hasher
->allocate_hash(hasher
, password
, password_hash
))
286 hasher
->destroy(hasher
);
289 hasher
->destroy(hasher
);
294 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
295 * username (without domain part)] with SHA1)
297 static status_t
ChallengeHash(chunk_t peer_challenge
, chunk_t server_challenge
,
298 chunk_t username
, chunk_t
*challenge_hash
)
302 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
305 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
308 concat
= chunk_cata("ccc", peer_challenge
, server_challenge
, username
);
309 if (!hasher
->allocate_hash(hasher
, concat
, challenge_hash
))
311 hasher
->destroy(hasher
);
314 hasher
->destroy(hasher
);
315 /* we need only the first 8 octets */
316 challenge_hash
->len
= 8;
321 * Calculate the challenge response (i.e. expand password_hash to three DES keys
322 * and then encrypt the 8-octet challenge_hash with these keys and concatenate
325 static status_t
ChallengeResponse(chunk_t challenge_hash
, chunk_t password_hash
,
330 chunk_t keys
[3], z_password_hash
;
331 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, ENCR_DES_ECB
, 8);
334 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
337 /* prepare keys: first pad password_hash to 21 octets, these get then split
338 * into 7-octet chunks, which then get expanded into 8-octet DES keys */
339 z_password_hash
= chunk_alloca(21);
340 memset(z_password_hash
.ptr
, 0, z_password_hash
.len
);
341 memcpy(z_password_hash
.ptr
, password_hash
.ptr
, password_hash
.len
);
342 chunk_split(z_password_hash
, "mmm", 7, &keys
[0], 7, &keys
[1], 7, &keys
[2]);
344 *response
= chunk_alloc(24);
345 for (i
= 0; i
< 3; i
++)
347 chunk_t expanded
, encrypted
;
349 expanded
= ExpandDESKey(keys
[i
]);
350 if (!crypter
->set_key(crypter
, expanded
) ||
351 !crypter
->encrypt(crypter
, challenge_hash
, chunk_empty
, &encrypted
))
353 chunk_clear(&expanded
);
354 crypter
->destroy(crypter
);
357 memcpy(&response
->ptr
[i
* 8], encrypted
.ptr
, encrypted
.len
);
358 chunk_clear(&encrypted
);
359 chunk_clear(&expanded
);
361 crypter
->destroy(crypter
);
366 * Computes the authenticator response
368 static status_t
AuthenticatorResponse(chunk_t password_hash_hash
,
369 chunk_t challenge_hash
, chunk_t nt_response
, chunk_t
*response
)
371 chunk_t magic1
= chunk_from_chars(
372 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
373 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
374 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
375 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
376 chunk_t magic2
= chunk_from_chars(
377 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
378 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
379 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
380 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
382 chunk_t digest
= chunk_empty
, concat
;
385 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
388 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
392 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
393 if (!hasher
->allocate_hash(hasher
, concat
, &digest
))
395 hasher
->destroy(hasher
);
398 concat
= chunk_cata("ccc", digest
, challenge_hash
, magic2
);
399 if (!hasher
->allocate_hash(hasher
, concat
, response
))
401 hasher
->destroy(hasher
);
404 hasher
->destroy(hasher
);
410 * Generate the master session key according to RFC3079
412 static status_t
GenerateMSK(chunk_t password_hash_hash
,
413 chunk_t nt_response
, chunk_t
*msk
)
415 chunk_t magic1
= chunk_from_chars(
416 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
417 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
418 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
419 chunk_t magic2
= chunk_from_chars(
420 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
421 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
422 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
423 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
424 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
425 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
426 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
427 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
428 0x6b, 0x65, 0x79, 0x2e);
429 chunk_t magic3
= chunk_from_chars(
430 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
431 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
432 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
433 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
434 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
435 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
436 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
437 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
438 0x6b, 0x65, 0x79, 0x2e);
439 chunk_t shapad1
= chunk_from_chars(
440 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
444 chunk_t shapad2
= chunk_from_chars(
445 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
446 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
447 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
448 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);
449 chunk_t keypad
= chunk_from_chars(
450 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
452 char master_key
[HASH_SIZE_SHA1
];
453 char master_receive_key
[HASH_SIZE_SHA1
], master_send_key
[HASH_SIZE_SHA1
];
454 chunk_t concat
, master
;
457 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
460 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
464 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
465 if (!hasher
->get_hash(hasher
, concat
, master_key
))
467 hasher
->destroy(hasher
);
470 master
= chunk_create(master_key
, 16);
471 concat
= chunk_cata("cccc", master
, shapad1
, magic2
, shapad2
);
472 if (!hasher
->get_hash(hasher
, concat
, master_receive_key
))
474 hasher
->destroy(hasher
);
477 concat
= chunk_cata("cccc", master
, shapad1
, magic3
, shapad2
);
478 if (!hasher
->get_hash(hasher
, concat
, master_send_key
))
480 hasher
->destroy(hasher
);
484 *msk
= chunk_cat("cccc", chunk_create(master_receive_key
, 16),
485 chunk_create(master_send_key
, 16), keypad
, keypad
);
487 hasher
->destroy(hasher
);
491 static status_t
GenerateStuff(private_eap_mschapv2_t
*this,
492 chunk_t server_challenge
, chunk_t peer_challenge
,
493 chunk_t username
, chunk_t nt_hash
)
495 status_t status
= FAILED
;
496 chunk_t nt_hash_hash
= chunk_empty
, challenge_hash
= chunk_empty
;
498 if (NtPasswordHash(nt_hash
, &nt_hash_hash
) != SUCCESS
)
502 if (ChallengeHash(peer_challenge
, server_challenge
, username
,
503 &challenge_hash
) != SUCCESS
)
507 if (ChallengeResponse(challenge_hash
, nt_hash
,
508 &this->nt_response
) != SUCCESS
)
512 if (AuthenticatorResponse(nt_hash_hash
, challenge_hash
,
513 this->nt_response
, &this->auth_response
) != SUCCESS
)
517 if (GenerateMSK(nt_hash_hash
, this->nt_response
, &this->msk
) != SUCCESS
)
525 chunk_free(&nt_hash_hash
);
526 chunk_free(&challenge_hash
);
531 * Converts an ASCII string into a UTF-16 (little-endian) string
533 static chunk_t
ascii_to_unicode(chunk_t ascii
)
536 chunk_t unicode
= chunk_alloc(ascii
.len
* 2);
537 for (i
= 0; i
< ascii
.len
; i
++)
539 unicode
.ptr
[i
* 2] = ascii
.ptr
[i
];
540 unicode
.ptr
[i
* 2 + 1] = 0;
546 * sanitize a string for printing
548 static char* sanitize(char *str
)
564 * Returns a chunk of just the username part of the given user identity.
565 * Note: the chunk points to internal data of the given chunk
567 static chunk_t
extract_username(chunk_t id
)
571 has_domain
= (char*)memchr(id
.ptr
, '\\', id
.len
);
575 has_domain
++; /* skip the backslash */
576 len
= id
.len
- ((u_char
*)has_domain
- id
.ptr
);
577 return len
> 0 ?
chunk_create(has_domain
, len
) : chunk_empty
;
583 * Set the ms_length field using aligned write
585 static void set_ms_length(eap_mschapv2_header_t
*eap
, u_int16_t len
)
587 len
= htons(len
- 5);
588 memcpy(&eap
->ms_length
, &len
, sizeof(u_int16_t
));
591 METHOD(eap_method_t
, initiate_peer
, status_t
,
592 private_eap_mschapv2_t
*this, eap_payload_t
**out
)
594 /* peer never initiates */
598 METHOD(eap_method_t
, initiate_server
, status_t
,
599 private_eap_mschapv2_t
*this, eap_payload_t
**out
)
602 eap_mschapv2_header_t
*eap
;
603 eap_mschapv2_challenge_t
*cha
;
604 const char *name
= MSCHAPV2_HOST_NAME
;
605 u_int16_t len
= CHALLENGE_PAYLOAD_LEN
+ sizeof(MSCHAPV2_HOST_NAME
) - 1;
607 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
608 if (!rng
|| !rng
->allocate_bytes(rng
, CHALLENGE_LEN
, &this->challenge
))
610 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no challenge");
617 eap
->code
= EAP_REQUEST
;
618 eap
->identifier
= this->identifier
;
619 eap
->length
= htons(len
);
620 eap
->type
= EAP_MSCHAPV2
;
621 eap
->opcode
= MSCHAPV2_CHALLENGE
;
622 eap
->ms_chapv2_id
= this->mschapv2id
;
623 set_ms_length(eap
, len
);
625 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
626 cha
->value_size
= CHALLENGE_LEN
;
627 memcpy(cha
->challenge
, this->challenge
.ptr
, this->challenge
.len
);
628 memcpy(cha
->name
, name
, sizeof(MSCHAPV2_HOST_NAME
) - 1);
630 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
634 static bool get_nt_hash(private_eap_mschapv2_t
*this, identification_t
*me
,
635 identification_t
*other
, chunk_t
*nt_hash
)
637 shared_key_t
*shared
;
640 /* try to find a stored NT_HASH first */
641 shared
= lib
->credmgr
->get_shared(lib
->credmgr
, SHARED_NT_HASH
, me
, other
);
644 *nt_hash
= chunk_clone(shared
->get_key(shared
));
645 shared
->destroy(shared
);
649 /* fallback to plaintext password */
650 shared
= lib
->credmgr
->get_shared(lib
->credmgr
, SHARED_EAP
, me
, other
);
653 password
= ascii_to_unicode(shared
->get_key(shared
));
654 shared
->destroy(shared
);
656 if (NtPasswordHash(password
, nt_hash
) == SUCCESS
)
658 chunk_clear(&password
);
661 chunk_clear(&password
);
667 * Process MS-CHAPv2 Challenge Requests
669 static status_t
process_peer_challenge(private_eap_mschapv2_t
*this,
670 eap_payload_t
*in
, eap_payload_t
**out
)
673 eap_mschapv2_header_t
*eap
;
674 eap_mschapv2_challenge_t
*cha
;
675 eap_mschapv2_response_t
*res
;
676 chunk_t data
, peer_challenge
, userid
, username
, nt_hash
;
677 u_int16_t len
= RESPONSE_PAYLOAD_LEN
;
679 data
= in
->get_data(in
);
680 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
682 /* the name MUST be at least one octet long */
683 if (data
.len
< CHALLENGE_PAYLOAD_LEN
+ 1)
685 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
689 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
691 if (cha
->value_size
!= CHALLENGE_LEN
)
693 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
694 "invalid challenge size");
698 this->mschapv2id
= eap
->ms_chapv2_id
;
699 this->challenge
= chunk_clone(chunk_create(cha
->challenge
, CHALLENGE_LEN
));
701 peer_challenge
= chunk_alloca(CHALLENGE_LEN
);
702 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
703 if (!rng
|| !rng
->get_bytes(rng
, CHALLENGE_LEN
, peer_challenge
.ptr
))
705 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, allocating challenge failed");
711 if (!get_nt_hash(this, this->peer
, this->server
, &nt_hash
))
713 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
714 this->server
, this->peer
);
718 /* we transmit the whole user identity (including the domain part) but
719 * only use the user part when calculating the challenge hash */
720 userid
= this->peer
->get_encoding(this->peer
);
722 username
= extract_username(userid
);
724 if (GenerateStuff(this, this->challenge
, peer_challenge
,
725 username
, nt_hash
) != SUCCESS
)
727 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 generating NT-Response failed");
728 chunk_clear(&nt_hash
);
731 chunk_clear(&nt_hash
);
734 eap
->code
= EAP_RESPONSE
;
735 eap
->identifier
= this->identifier
;
736 eap
->length
= htons(len
);
737 eap
->type
= EAP_MSCHAPV2
;
738 eap
->opcode
= MSCHAPV2_RESPONSE
;
739 eap
->ms_chapv2_id
= this->mschapv2id
;
740 set_ms_length(eap
, len
);
742 res
= (eap_mschapv2_response_t
*)eap
->data
;
743 res
->value_size
= RESPONSE_LEN
;
744 memset(&res
->response
, 0, RESPONSE_LEN
);
745 memcpy(res
->response
.peer_challenge
, peer_challenge
.ptr
, peer_challenge
.len
);
746 memcpy(res
->response
.nt_response
, this->nt_response
.ptr
, this->nt_response
.len
);
747 memcpy(res
->name
, userid
.ptr
, userid
.len
);
749 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
754 * Process MS-CHAPv2 Success Requests
756 static status_t
process_peer_success(private_eap_mschapv2_t
*this,
757 eap_payload_t
*in
, eap_payload_t
**out
)
759 status_t status
= FAILED
;
760 enumerator_t
*enumerator
;
761 eap_mschapv2_header_t
*eap
;
762 chunk_t data
, auth_string
= chunk_empty
;
763 char *message
, *token
, *msg
= NULL
;
765 u_int16_t len
= SHORT_HEADER_LEN
;
767 data
= in
->get_data(in
);
768 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
770 if (data
.len
< AUTH_RESPONSE_LEN
)
772 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
776 message_len
= data
.len
- HEADER_LEN
;
777 message
= malloc(message_len
+ 1);
778 memcpy(message
, eap
->data
, message_len
);
779 message
[message_len
] = '\0';
781 /* S=<auth_string> M=<msg> */
782 enumerator
= enumerator_create_token(message
, " ", " ");
783 while (enumerator
->enumerate(enumerator
, &token
))
785 if (strpfx(token
, "S="))
789 if (strlen(token
) != AUTH_RESPONSE_LEN
- 2)
791 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
792 "invalid auth string");
795 chunk_free(&auth_string
);
796 hex
= chunk_create(token
, AUTH_RESPONSE_LEN
- 2);
797 auth_string
= chunk_from_hex(hex
, NULL
);
799 else if (strpfx(token
, "M="))
806 enumerator
->destroy(enumerator
);
808 if (auth_string
.ptr
== NULL
)
810 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
811 "auth string missing");
815 if (!chunk_equals_const(this->auth_response
, auth_string
))
817 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
821 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg
));
824 eap
->code
= EAP_RESPONSE
;
825 eap
->identifier
= this->identifier
;
826 eap
->length
= htons(len
);
827 eap
->type
= EAP_MSCHAPV2
;
828 eap
->opcode
= MSCHAPV2_SUCCESS
;
830 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
834 chunk_free(&auth_string
);
840 static status_t
process_peer_failure(private_eap_mschapv2_t
*this,
841 eap_payload_t
*in
, eap_payload_t
**out
)
843 status_t status
= FAILED
;
844 enumerator_t
*enumerator
;
845 eap_mschapv2_header_t
*eap
;
847 char *message
, *token
, *msg
= NULL
;
848 int message_len
, error
= 0;
849 chunk_t challenge
= chunk_empty
;
851 data
= in
->get_data(in
);
852 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
854 if (data
.len
< 3) /* we want at least an error code: E=e */
856 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
860 message_len
= data
.len
- HEADER_LEN
;
861 message
= malloc(message_len
+ 1);
862 memcpy(message
, eap
->data
, message_len
);
863 message
[message_len
] = '\0';
865 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
866 enumerator
= enumerator_create_token(message
, " ", " ");
867 while (enumerator
->enumerate(enumerator
, &token
))
869 if (strpfx(token
, "E="))
874 else if (strpfx(token
, "R="))
876 /* ignore retriable */
878 else if (strpfx(token
, "C="))
882 if (strlen(token
) != 2 * CHALLENGE_LEN
)
884 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message:"
885 "invalid challenge");
888 chunk_free(&challenge
);
889 hex
= chunk_create(token
, 2 * CHALLENGE_LEN
);
890 challenge
= chunk_from_hex(hex
, NULL
);
892 else if (strpfx(token
, "V="))
896 else if (strpfx(token
, "M="))
903 enumerator
->destroy(enumerator
);
905 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed with error %N: '%s'",
906 mschapv2_error_names
, error
, sanitize(msg
));
909 * at this point, if the error is retriable, we MAY retry the authentication
910 * or MAY send a Change Password packet.
912 * if the error is not retriable (or if we do neither of the above), we
913 * SHOULD send a Failure Response packet.
914 * windows clients don't do that, and since windows server 2008 r2 behaves
915 * pretty odd if we do send a Failure Response, we just don't send one
916 * either. windows 7 actually sends a delete notify (which, according to the
917 * logs, results in an error on windows server 2008 r2).
919 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
920 * a disabled account but returns the windows error code in a notify payload
927 chunk_free(&challenge
);
933 METHOD(eap_method_t
, process_peer
, status_t
,
934 private_eap_mschapv2_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
937 eap_mschapv2_header_t
*eap
;
939 this->identifier
= in
->get_identifier(in
);
940 data
= in
->get_data(in
);
941 if (data
.len
< SHORT_HEADER_LEN
)
943 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message");
947 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
951 case MSCHAPV2_CHALLENGE
:
953 return process_peer_challenge(this, in
, out
);
955 case MSCHAPV2_SUCCESS
:
957 return process_peer_success(this, in
, out
);
959 case MSCHAPV2_FAILURE
:
961 return process_peer_failure(this, in
, out
);
965 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
966 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
974 * Handles retries on the server
976 static status_t
process_server_retry(private_eap_mschapv2_t
*this,
979 eap_mschapv2_header_t
*eap
;
982 char msg
[FAILURE_MESSAGE_LEN
];
983 u_int16_t len
= HEADER_LEN
+ FAILURE_MESSAGE_LEN
- 1; /* no null byte */
985 if (++this->retries
> MAX_RETRIES
)
987 /* we MAY send a Failure Request with R=0, but windows 7 does not
988 * really like that and does not respond with a Failure Response.
989 * so, to clean up our state we just fail with an EAP-Failure.
990 * this gives an unknown error on the windows side, but is also fine
991 * with the standard. */
992 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed: "
993 "maximum number of retries reached");
997 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries
);
999 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
1000 if (!rng
|| !rng
->get_bytes(rng
, CHALLENGE_LEN
, this->challenge
.ptr
))
1002 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, allocating challenge failed");
1008 chunk_free(&this->nt_response
);
1009 chunk_free(&this->auth_response
);
1010 chunk_free(&this->msk
);
1013 eap
->code
= EAP_REQUEST
;
1014 eap
->identifier
= ++this->identifier
;
1015 eap
->length
= htons(len
);
1016 eap
->type
= EAP_MSCHAPV2
;
1017 eap
->opcode
= MSCHAPV2_FAILURE
;
1018 eap
->ms_chapv2_id
= this->mschapv2id
++; /* increase for each retry */
1019 set_ms_length(eap
, len
);
1021 hex
= chunk_to_hex(this->challenge
, NULL
, TRUE
);
1022 snprintf(msg
, FAILURE_MESSAGE_LEN
, "%s%s", FAILURE_MESSAGE
, hex
.ptr
);
1024 memcpy(eap
->data
, msg
, FAILURE_MESSAGE_LEN
- 1); /* no null byte */
1025 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1027 /* delay the response for some time to make brute-force attacks harder */
1034 * Process MS-CHAPv2 Response response packets
1036 static status_t
process_server_response(private_eap_mschapv2_t
*this,
1037 eap_payload_t
*in
, eap_payload_t
**out
)
1039 eap_mschapv2_header_t
*eap
;
1040 eap_mschapv2_response_t
*res
;
1041 chunk_t data
, peer_challenge
, username
, nt_hash
;
1042 identification_t
*userid
;
1046 data
= in
->get_data(in
);
1047 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1049 if (data
.len
< RESPONSE_PAYLOAD_LEN
)
1051 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1055 res
= (eap_mschapv2_response_t
*)eap
->data
;
1056 peer_challenge
= chunk_create(res
->response
.peer_challenge
, CHALLENGE_LEN
);
1058 name_len
= min(data
.len
- RESPONSE_PAYLOAD_LEN
, 255);
1059 snprintf(buf
, sizeof(buf
), "%.*s", name_len
, res
->name
);
1060 userid
= identification_create_from_string(buf
);
1061 DBG2(DBG_IKE
, "EAP-MS-CHAPv2 username: '%Y'", userid
);
1062 /* userid can only be destroyed after the last use of username */
1063 username
= extract_username(userid
->get_encoding(userid
));
1065 if (!get_nt_hash(this, this->server
, userid
, &nt_hash
))
1067 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
1068 this->server
, userid
);
1069 /* FIXME: windows 7 always sends the username that is first entered in
1070 * the username box, even, if the user changes it during retries (probably
1071 * to keep consistent with the EAP-Identity).
1072 * thus, we could actually fail here, because retries do not make much
1073 * sense. on the other hand, an attacker could guess usernames, if the
1074 * error messages were different. */
1075 userid
->destroy(userid
);
1076 return process_server_retry(this, out
);
1079 if (GenerateStuff(this, this->challenge
, peer_challenge
,
1080 username
, nt_hash
) != SUCCESS
)
1082 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
1083 userid
->destroy(userid
);
1084 chunk_clear(&nt_hash
);
1087 userid
->destroy(userid
);
1088 chunk_clear(&nt_hash
);
1090 if (memeq_const(res
->response
.nt_response
, this->nt_response
.ptr
,
1091 this->nt_response
.len
))
1094 char msg
[AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
)];
1095 u_int16_t len
= HEADER_LEN
+ AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
);
1098 eap
->code
= EAP_REQUEST
;
1099 eap
->identifier
= ++this->identifier
;
1100 eap
->length
= htons(len
);
1101 eap
->type
= EAP_MSCHAPV2
;
1102 eap
->opcode
= MSCHAPV2_SUCCESS
;
1103 eap
->ms_chapv2_id
= this->mschapv2id
;
1104 set_ms_length(eap
, len
);
1106 hex
= chunk_to_hex(this->auth_response
, NULL
, TRUE
);
1107 snprintf(msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
),
1108 "S=%s%s", hex
.ptr
, SUCCESS_MESSAGE
);
1110 memcpy(eap
->data
, msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
));
1111 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1115 return process_server_retry(this, out
);
1118 METHOD(eap_method_t
, process_server
, status_t
,
1119 private_eap_mschapv2_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
1121 eap_mschapv2_header_t
*eap
;
1124 if (this->identifier
!= in
->get_identifier(in
))
1126 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
1127 "unexpected identifier");
1131 data
= in
->get_data(in
);
1132 if (data
.len
< SHORT_HEADER_LEN
)
1134 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1138 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1140 switch (eap
->opcode
)
1142 case MSCHAPV2_RESPONSE
:
1144 return process_server_response(this, in
, out
);
1146 case MSCHAPV2_SUCCESS
:
1150 case MSCHAPV2_FAILURE
:
1156 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
1157 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
1164 METHOD(eap_method_t
, get_type
, eap_type_t
,
1165 private_eap_mschapv2_t
*this, u_int32_t
*vendor
)
1168 return EAP_MSCHAPV2
;
1171 METHOD(eap_method_t
, get_msk
, status_t
,
1172 private_eap_mschapv2_t
*this, chunk_t
*msk
)
1182 METHOD(eap_method_t
, get_identifier
, u_int8_t
,
1183 private_eap_mschapv2_t
*this)
1185 return this->identifier
;
1188 METHOD(eap_method_t
, set_identifier
, void,
1189 private_eap_mschapv2_t
*this, u_int8_t identifier
)
1191 this->identifier
= identifier
;
1194 METHOD(eap_method_t
, is_mutual
, bool,
1195 private_eap_mschapv2_t
*this)
1200 METHOD(eap_method_t
, destroy
, void,
1201 private_eap_mschapv2_t
*this)
1203 this->peer
->destroy(this->peer
);
1204 this->server
->destroy(this->server
);
1205 chunk_free(&this->challenge
);
1206 chunk_free(&this->nt_response
);
1207 chunk_free(&this->auth_response
);
1208 chunk_free(&this->msk
);
1213 * Generic constructor
1215 static private_eap_mschapv2_t
*eap_mschapv2_create_generic(identification_t
*server
, identification_t
*peer
)
1217 private_eap_mschapv2_t
*this;
1221 .eap_method_interface
= {
1222 .get_type
= _get_type
,
1223 .is_mutual
= _is_mutual
,
1224 .get_msk
= _get_msk
,
1225 .get_identifier
= _get_identifier
,
1226 .set_identifier
= _set_identifier
,
1227 .destroy
= _destroy
,
1230 .peer
= peer
->clone(peer
),
1231 .server
= server
->clone(server
),
1240 eap_mschapv2_t
*eap_mschapv2_create_server(identification_t
*server
, identification_t
*peer
)
1242 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
1244 this->public.eap_method_interface
.initiate
= _initiate_server
;
1245 this->public.eap_method_interface
.process
= _process_server
;
1247 /* generate a non-zero identifier */
1250 this->identifier
= random();
1251 } while (!this->identifier
);
1253 this->mschapv2id
= this->identifier
;
1255 return &this->public;
1261 eap_mschapv2_t
*eap_mschapv2_create_peer(identification_t
*server
, identification_t
*peer
)
1263 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
1265 this->public.eap_method_interface
.initiate
= _initiate_peer
;
1266 this->public.eap_method_interface
.process
= _process_peer
;
1268 return &this->public;