2 * Copyright (C) 2009 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "eap_mschapv2.h"
23 #include <utils/enumerator.h>
24 #include <crypto/crypters/crypter.h>
25 #include <crypto/hashers/hasher.h>
27 typedef struct private_eap_mschapv2_t private_eap_mschapv2_t
;
30 * Private data of an eap_mschapv2_t object.
32 struct private_eap_mschapv2_t
35 * Public authenticator_t interface.
37 eap_mschapv2_t
public;
42 identification_t
*server
;
47 identification_t
*peer
;
50 * challenge sent by the server
55 * generated NT-Response
60 * generated Authenticator Response
62 chunk_t auth_response
;
70 * EAP message identifier
75 * MS-CHAPv2-ID (session ID, increases with each retry)
88 enum mschapv2_opcode_t
90 MSCHAPV2_CHALLENGE
= 1,
91 MSCHAPV2_RESPONSE
= 2,
94 MSCHAPV2_CHANGE_PASSWORD
= 7,
100 ENUM_BEGIN(mschapv2_opcode_names
, MSCHAPV2_CHALLENGE
, MSCHAPV2_FAILURE
,
105 ENUM_NEXT(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_FAILURE
,
107 ENUM_END(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
);
112 enum mschapv2_error_t
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,
123 * Names for error codes
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
);
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)
151 /* Number of seconds to delay retries */
152 #define RETRY_DELAY 2
153 /* Maximum number of retries */
154 #define MAX_RETRIES 2
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
;
161 * packed EAP-MS-CHAPv2 header struct
163 struct eap_mschapv2_header_t
165 /** EAP code (REQUEST/RESPONSE) */
167 /** unique message identifier */
169 /** length of whole message */
173 /** MS-CHAPv2 OpCode */
175 /** MS-CHAPv2-ID (equals identifier) */
176 u_int8_t ms_chapv2_id
;
177 /** MS-Length (defined as length - 5) */
179 /** packet data (determined by OpCode) */
181 }__attribute__((__packed__
));
184 * packed data for a MS-CHAPv2 Challenge packet
186 struct eap_mschapv2_challenge_t
191 u_int8_t challenge
[CHALLENGE_LEN
];
194 }__attribute__((__packed__
));
197 * packed data for a MS-CHAPv2 Response packet
199 struct eap_mschapv2_response_t
207 u_int8_t peer_challenge
[CHALLENGE_LEN
];
208 /* Reserved (=zero) */
209 u_int8_t peer_reserved
[8];
211 u_int8_t nt_response
[24];
217 }__attribute__((__packed__
));
220 * Length of the MS-CHAPv2 header
222 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
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)
228 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
231 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
232 * at least one octet)
234 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
237 * The minimum length of an MS-CHAPv2 Response packet
239 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
243 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
245 static chunk_t
ExpandDESKey(chunk_t key
)
247 static const u_char bitmask
[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
252 /* expand the 7 octets to 8 octets */
253 expanded
= chunk_alloc(8);
254 for (i
= 0; i
< 7; i
++)
256 expanded
.ptr
[i
] = ((key
.ptr
[i
] & bitmask
[i
]) >> i
) | (carry
<< (8 - i
));
257 carry
= key
.ptr
[i
] & ~bitmask
[i
];
259 expanded
.ptr
[7] = carry
<< 1;
261 /* add parity bits to each octet */
262 for (i
= 0; i
< 8; i
++)
264 u_char val
= expanded
.ptr
[i
];
265 val
= (val
^ (val
>> 4)) & 0x0f;
266 expanded
.ptr
[i
] |= (0x9669 >> val
) & 1;
272 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
274 static status_t
NtPasswordHash(chunk_t password
, chunk_t
*password_hash
)
277 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD4
);
280 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
283 hasher
->allocate_hash(hasher
, password
, password_hash
);
284 hasher
->destroy(hasher
);
289 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
290 * username (without domain part)] with SHA1)
292 static status_t
ChallengeHash(chunk_t peer_challenge
, chunk_t server_challenge
,
293 chunk_t username
, chunk_t
*challenge_hash
)
297 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
300 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
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;
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
316 static status_t
ChallengeResponse(chunk_t challenge_hash
, chunk_t password_hash
,
321 chunk_t keys
[3], z_password_hash
;
322 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, ENCR_DES_ECB
, 8);
325 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
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]);
335 *response
= chunk_alloc(24);
336 for (i
= 0; i
< 3; i
++)
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
);
346 crypter
->destroy(crypter
);
351 * Computes the authenticator response
353 static status_t
AuthenticatorResponse(chunk_t password_hash_hash
,
354 chunk_t challenge_hash
, chunk_t nt_response
, chunk_t
*response
)
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,
367 chunk_t digest
= chunk_empty
, concat
;
370 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
373 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
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
);
382 hasher
->destroy(hasher
);
388 * Generate the master session key according to RFC3079
390 static status_t
GenerateMSK(chunk_t password_hash_hash
,
391 chunk_t nt_response
, chunk_t
*msk
)
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
;
433 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
436 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
440 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
441 hasher
->allocate_hash(hasher
, concat
, &master_key
);
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;
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;
452 *msk
= chunk_cat("cccc", master_receive_key
, master_send_key
, keypad
, keypad
);
454 hasher
->destroy(hasher
);
455 chunk_free(&master_key
);
456 chunk_free(&master_receive_key
);
457 chunk_free(&master_send_key
);
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
)
465 status_t status
= FAILED
;
466 chunk_t password_hash
= chunk_empty
, password_hash_hash
= chunk_empty
,
467 challenge_hash
= chunk_empty
;
469 if (NtPasswordHash(password
, &password_hash
) != SUCCESS
)
473 if (NtPasswordHash(password_hash
, &password_hash_hash
) != SUCCESS
)
477 if (ChallengeHash(peer_challenge
, server_challenge
, username
,
478 &challenge_hash
) != SUCCESS
)
483 if (ChallengeResponse(challenge_hash
, password_hash
,
484 &this->nt_response
) != SUCCESS
)
488 if (AuthenticatorResponse(password_hash_hash
, challenge_hash
,
489 this->nt_response
, &this->auth_response
) != SUCCESS
)
493 if (GenerateMSK(password_hash_hash
, this->nt_response
, &this->msk
) != SUCCESS
)
501 chunk_free(&password_hash
);
502 chunk_free(&password_hash_hash
);
503 chunk_free(&challenge_hash
);
508 * Converts an ASCII string into a UTF-16 (little-endian) string
510 static chunk_t
ascii_to_unicode(chunk_t ascii
)
513 chunk_t unicode
= chunk_alloc(ascii
.len
* 2);
514 for (i
= 0; i
< ascii
.len
; i
++)
516 unicode
.ptr
[i
* 2] = ascii
.ptr
[i
];
517 unicode
.ptr
[i
* 2 + 1] = 0;
523 * sanitize a string for printing
525 static char* sanitize(char *str
)
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.
544 static chunk_t
extract_username(identification_t
* identification
)
548 id
= identification
->get_encoding(identification
);
549 has_domain
= (char*)memchr(id
.ptr
, '\\', id
.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
;
561 * Set the ms_length field using aligned write
563 static void set_ms_length(eap_mschapv2_header_t
*eap
, u_int16_t len
)
565 len
= htons(len
- 5);
566 memcpy(&eap
->ms_length
, &len
, sizeof(u_int16_t
));
570 * Implementation of eap_method_t.initiate for the peer
572 static status_t
initiate_peer(private_eap_mschapv2_t
*this, eap_payload_t
**out
)
574 /* peer never initiates */
579 * Implementation of eap_method_t.initiate for the server
581 static status_t
initiate_server(private_eap_mschapv2_t
*this, eap_payload_t
**out
)
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;
589 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
592 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no RNG");
595 rng
->allocate_bytes(rng
, CHALLENGE_LEN
, &this->challenge
);
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
);
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);
612 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
618 * Process MS-CHAPv2 Challenge Requests
620 static status_t
process_peer_challenge(private_eap_mschapv2_t
*this,
621 eap_payload_t
*in
, eap_payload_t
**out
)
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
;
631 data
= in
->get_data(in
);
632 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
634 /* the name MUST be at least one octet long */
635 if (data
.len
< CHALLENGE_PAYLOAD_LEN
+ 1)
637 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
641 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
643 if (cha
->value_size
!= CHALLENGE_LEN
)
645 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
646 "invalid challenge size");
650 this->mschapv2id
= eap
->ms_chapv2_id
;
651 this->challenge
= chunk_clone(chunk_create(cha
->challenge
, CHALLENGE_LEN
));
653 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
656 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no RNG");
659 peer_challenge
= chunk_alloca(CHALLENGE_LEN
);
660 rng
->get_bytes(rng
, CHALLENGE_LEN
, peer_challenge
.ptr
);
663 shared
= charon
->credentials
->get_shared(charon
->credentials
,
664 SHARED_EAP
, this->peer
, this->server
);
667 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
668 this->server
, this->peer
);
672 password
= ascii_to_unicode(shared
->get_key(shared
));
673 shared
->destroy(shared
);
675 username
= extract_username(this->peer
);
678 if (GenerateStuff(this, this->challenge
, peer_challenge
, username
, password
) != SUCCESS
)
680 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 generating NT-Response failed");
681 chunk_clear(&password
);
684 chunk_clear(&password
);
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
);
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
);
701 username
= this->peer
->get_encoding(this->peer
);
702 memcpy(res
->name
, username
.ptr
, username
.len
);
704 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
709 * Process MS-CHAPv2 Success Requests
711 static status_t
process_peer_success(private_eap_mschapv2_t
*this,
712 eap_payload_t
*in
, eap_payload_t
**out
)
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
;
720 u_int16_t len
= SHORT_HEADER_LEN
;
722 data
= in
->get_data(in
);
723 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
725 if (data
.len
< AUTH_RESPONSE_LEN
)
727 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
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';
736 /* S=<auth_string> M=<msg> */
737 enumerator
= enumerator_create_token(message
, " ", " ");
738 while (enumerator
->enumerate(enumerator
, &token
))
740 if (strneq(token
, "S=", 2))
744 if (strlen(token
) != AUTH_RESPONSE_LEN
- 2)
746 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
747 "invalid auth string");
750 hex
= chunk_create(token
, AUTH_RESPONSE_LEN
- 2);
751 auth_string
= chunk_from_hex(hex
, NULL
);
753 else if (strneq(token
, "M=", 2))
759 enumerator
->destroy(enumerator
);
761 if (auth_string
.ptr
== NULL
)
763 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
764 "auth string missing");
768 if (!chunk_equals(this->auth_response
, auth_string
))
770 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
774 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg
));
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
;
783 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
787 chunk_free(&auth_string
);
793 static status_t
process_peer_failure(private_eap_mschapv2_t
*this,
794 eap_payload_t
*in
, eap_payload_t
**out
)
796 status_t status
= FAILED
;
797 enumerator_t
*enumerator
;
798 eap_mschapv2_header_t
*eap
;
800 char *message
, *token
, *msg
= NULL
;
801 int message_len
, error
, retryable
;
802 chunk_t challenge
= chunk_empty
;
804 data
= in
->get_data(in
);
805 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
807 if (data
.len
< 3) /* we want at least an error code: E=e */
809 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
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';
818 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
819 enumerator
= enumerator_create_token(message
, " ", " ");
820 while (enumerator
->enumerate(enumerator
, &token
))
822 if (strneq(token
, "E=", 2))
827 else if (strneq(token
, "R=", 2))
830 retryable
= atoi(token
);
832 else if (strneq(token
, "C=", 2))
836 if (strlen(token
) != 2 * CHALLENGE_LEN
)
838 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message:"
839 "invalid challenge");
842 hex
= chunk_create(token
, 2 * CHALLENGE_LEN
);
843 challenge
= chunk_from_hex(hex
, NULL
);
845 else if (strneq(token
, "V=", 2))
849 version
= atoi(token
);
851 else if (strneq(token
, "M=", 2))
857 enumerator
->destroy(enumerator
);
859 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed with error %N: '%s'",
860 mschapv2_error_names
, error
, sanitize(msg
));
863 * at this point, if the error is retryable, we MAY retry the authentication
864 * or MAY send a Change Password packet.
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).
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
881 chunk_free(&challenge
);
888 * Implementation of eap_method_t.process for the peer
890 static status_t
process_peer(private_eap_mschapv2_t
*this, eap_payload_t
*in
,
894 eap_mschapv2_header_t
*eap
;
896 this->identifier
= in
->get_identifier(in
);
897 data
= in
->get_data(in
);
898 if (data
.len
< SHORT_HEADER_LEN
)
900 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message");
904 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
908 case MSCHAPV2_CHALLENGE
:
910 return process_peer_challenge(this, in
, out
);
912 case MSCHAPV2_SUCCESS
:
914 return process_peer_success(this, in
, out
);
916 case MSCHAPV2_FAILURE
:
918 return process_peer_failure(this, in
, out
);
922 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
923 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
931 * Handles retries on the server
933 static status_t
process_server_retry(private_eap_mschapv2_t
*this,
936 eap_mschapv2_header_t
*eap
;
939 char msg
[FAILURE_MESSAGE_LEN
];
940 u_int16_t len
= HEADER_LEN
+ FAILURE_MESSAGE_LEN
- 1; /* no null byte */
942 if (++this->retries
> MAX_RETRIES
)
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");
954 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries
);
956 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
959 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no RNG");
962 rng
->get_bytes(rng
, CHALLENGE_LEN
, this->challenge
.ptr
);
965 chunk_free(&this->nt_response
);
966 chunk_free(&this->auth_response
);
967 chunk_free(&this->msk
);
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
);
978 hex
= chunk_to_hex(this->challenge
, NULL
, TRUE
);
979 snprintf(msg
, FAILURE_MESSAGE_LEN
, "%s%s", FAILURE_MESSAGE
, hex
.ptr
);
981 memcpy(eap
->data
, msg
, FAILURE_MESSAGE_LEN
- 1); /* no null byte */
982 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
984 /* delay the response for some time to make brute-force attacks harder */
991 * Process MS-CHAPv2 Response response packets
993 static status_t
process_server_response(private_eap_mschapv2_t
*this,
994 eap_payload_t
*in
, eap_payload_t
**out
)
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
;
1004 data
= in
->get_data(in
);
1005 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1007 if (data
.len
< RESPONSE_PAYLOAD_LEN
)
1009 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1013 res
= (eap_mschapv2_response_t
*)eap
->data
;
1014 peer_challenge
= chunk_create(res
->response
.peer_challenge
, CHALLENGE_LEN
);
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
);
1022 shared
= charon
->credentials
->get_shared(charon
->credentials
,
1023 SHARED_EAP
, this->server
, userid
);
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
);
1038 password
= ascii_to_unicode(shared
->get_key(shared
));
1039 shared
->destroy(shared
);
1041 if (GenerateStuff(this, this->challenge
, peer_challenge
,
1042 username
, password
) != SUCCESS
)
1044 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
1045 userid
->destroy(userid
);
1046 chunk_clear(&password
);
1049 userid
->destroy(userid
);
1050 chunk_clear(&password
);
1052 if (memeq(res
->response
.nt_response
, this->nt_response
.ptr
, this->nt_response
.len
))
1055 char msg
[AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
)];
1056 u_int16_t len
= HEADER_LEN
+ AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
);
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
);
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
);
1071 memcpy(eap
->data
, msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
));
1072 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1076 return process_server_retry(this, out
);
1080 * Implementation of eap_method_t.process for the server
1082 static status_t
process_server(private_eap_mschapv2_t
*this, eap_payload_t
*in
,
1083 eap_payload_t
**out
)
1085 eap_mschapv2_header_t
*eap
;
1088 if (this->identifier
!= in
->get_identifier(in
))
1090 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
1091 "unexpected identifier");
1095 data
= in
->get_data(in
);
1096 if (data
.len
< SHORT_HEADER_LEN
)
1098 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1102 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1104 switch (eap
->opcode
)
1106 case MSCHAPV2_RESPONSE
:
1108 return process_server_response(this, in
, out
);
1110 case MSCHAPV2_SUCCESS
:
1114 case MSCHAPV2_FAILURE
:
1120 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
1121 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
1129 * Implementation of eap_method_t.get_type.
1131 static eap_type_t
get_type(private_eap_mschapv2_t
*this, u_int32_t
*vendor
)
1134 return EAP_MSCHAPV2
;
1138 * Implementation of eap_method_t.get_msk.
1140 static status_t
get_msk(private_eap_mschapv2_t
*this, chunk_t
*msk
)
1151 * Implementation of eap_method_t.is_mutual.
1153 static bool is_mutual(private_eap_mschapv2_t
*this)
1159 * Implementation of eap_method_t.destroy.
1161 static void destroy(private_eap_mschapv2_t
*this)
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
);
1173 * Generic constructor
1175 static private_eap_mschapv2_t
*eap_mschapv2_create_generic(identification_t
*server
, identification_t
*peer
)
1177 private_eap_mschapv2_t
*this = malloc_thing(private_eap_mschapv2_t
);
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
;
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;
1203 eap_mschapv2_t
*eap_mschapv2_create_server(identification_t
*server
, identification_t
*peer
)
1205 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
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
;
1210 /* generate a non-zero identifier */
1213 this->identifier
= random();
1214 } while (!this->identifier
);
1216 this->mschapv2id
= this->identifier
;
1218 return &this->public;
1224 eap_mschapv2_t
*eap_mschapv2_create_peer(identification_t
*server
, identification_t
*peer
)
1226 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
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
;
1231 return &this->public;