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)
86 * Provide EAP-Identity
94 enum mschapv2_opcode_t
96 MSCHAPV2_CHALLENGE
= 1,
97 MSCHAPV2_RESPONSE
= 2,
100 MSCHAPV2_CHANGE_PASSWORD
= 7,
106 ENUM_BEGIN(mschapv2_opcode_names
, MSCHAPV2_CHALLENGE
, MSCHAPV2_FAILURE
,
111 ENUM_NEXT(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_FAILURE
,
113 ENUM_END(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
);
118 enum mschapv2_error_t
120 ERROR_RESTRICTED_LOGON_HOURS
= 646,
121 ERROR_ACCT_DISABLED
= 647,
122 ERROR_PASSWD_EXPIRED
= 648,
123 ERROR_NO_DIALIN_PERMISSION
= 649,
124 ERROR_AUTHENTICATION_FAILURE
= 691,
125 ERROR_CHANGING_PASSWORD
= 709,
129 * Names for error codes
131 ENUM_BEGIN(mschapv2_error_names
, ERROR_RESTRICTED_LOGON_HOURS
, ERROR_NO_DIALIN_PERMISSION
,
132 "ERROR_RESTRICTED_LOGON_HOURS",
133 "ERROR_ACCT_DISABLED",
134 "ERROR_PASSWD_EXPIRED",
135 "ERROR_NO_DIALIN_PERMISSION");
136 ENUM_NEXT(mschapv2_error_names
, ERROR_AUTHENTICATION_FAILURE
, ERROR_AUTHENTICATION_FAILURE
, ERROR_NO_DIALIN_PERMISSION
,
137 "ERROR_AUTHENTICATION_FAILURE");
138 ENUM_NEXT(mschapv2_error_names
, ERROR_CHANGING_PASSWORD
, ERROR_CHANGING_PASSWORD
, ERROR_AUTHENTICATION_FAILURE
,
139 "ERROR_CHANGING_PASSWORD");
140 ENUM_END(mschapv2_error_names
, ERROR_CHANGING_PASSWORD
);
142 /* Length of the challenge */
143 #define CHALLENGE_LEN 16
144 /* Length of the response (see eap_mschapv2_response_t) */
145 #define RESPONSE_LEN 49
146 /* Length of the authenticator response string ("S=<...>") */
147 #define AUTH_RESPONSE_LEN 42
148 /* Name we send as authenticator */
149 #define MSCHAPV2_HOST_NAME "strongSwan"
150 /* Message sent on success */
151 #define SUCCESS_MESSAGE " M=Welcome2strongSwan"
152 /* Message sent on failure */
153 #define FAILURE_MESSAGE "E=691 R=1 C="
154 /* Length of the complete failure message */
155 #define FAILURE_MESSAGE_LEN (sizeof(FAILURE_MESSAGE) + CHALLENGE_LEN * 2)
157 /* Number of seconds to delay retries */
158 #define RETRY_DELAY 2
159 /* Maximum number of retries */
160 #define MAX_RETRIES 2
162 typedef struct eap_mschapv2_header_t eap_mschapv2_header_t
;
163 typedef struct eap_mschapv2_challenge_t eap_mschapv2_challenge_t
;
164 typedef struct eap_mschapv2_response_t eap_mschapv2_response_t
;
167 * packed EAP-MS-CHAPv2 header struct
169 struct eap_mschapv2_header_t
171 /** EAP code (REQUEST/RESPONSE) */
173 /** unique message identifier */
175 /** length of whole message */
179 /** MS-CHAPv2 OpCode */
181 /** MS-CHAPv2-ID (equals identifier) */
182 u_int8_t ms_chapv2_id
;
183 /** MS-Length (defined as length - 5) */
185 /** packet data (determined by OpCode) */
187 }__attribute__((__packed__
));
190 * packed data for a MS-CHAPv2 Challenge packet
192 struct eap_mschapv2_challenge_t
197 u_int8_t challenge
[CHALLENGE_LEN
];
200 }__attribute__((__packed__
));
203 * packed data for a MS-CHAPv2 Response packet
205 struct eap_mschapv2_response_t
213 u_int8_t peer_challenge
[CHALLENGE_LEN
];
214 /* Reserved (=zero) */
215 u_int8_t peer_reserved
[8];
217 u_int8_t nt_response
[24];
223 }__attribute__((__packed__
));
226 * Length of the MS-CHAPv2 header
228 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
231 * Length of the header for MS-CHAPv2 success/failure packets (does not include
232 * MS-CHAPv2-ID and MS-Length, i.e. 3 octets)
234 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
237 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
238 * at least one octet)
240 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
243 * The minimum length of an MS-CHAPv2 Response packet
245 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
249 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
251 static chunk_t
ExpandDESKey(chunk_t key
)
253 static const u_char bitmask
[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
258 /* expand the 7 octets to 8 octets */
259 expanded
= chunk_alloc(8);
260 for (i
= 0; i
< 7; i
++)
262 expanded
.ptr
[i
] = ((key
.ptr
[i
] & bitmask
[i
]) >> i
) | (carry
<< (8 - i
));
263 carry
= key
.ptr
[i
] & ~bitmask
[i
];
265 expanded
.ptr
[7] = carry
<< 1;
267 /* add parity bits to each octet */
268 for (i
= 0; i
< 8; i
++)
270 u_char val
= expanded
.ptr
[i
];
271 val
= (val
^ (val
>> 4)) & 0x0f;
272 expanded
.ptr
[i
] |= (0x9669 >> val
) & 1;
278 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
280 static status_t
NtPasswordHash(chunk_t password
, chunk_t
*password_hash
)
283 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD4
);
286 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
289 if (!hasher
->allocate_hash(hasher
, password
, password_hash
))
291 hasher
->destroy(hasher
);
294 hasher
->destroy(hasher
);
299 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
300 * username (without domain part)] with SHA1)
302 static status_t
ChallengeHash(chunk_t peer_challenge
, chunk_t server_challenge
,
303 chunk_t username
, chunk_t
*challenge_hash
)
307 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
310 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
313 concat
= chunk_cata("ccc", peer_challenge
, server_challenge
, username
);
314 if (!hasher
->allocate_hash(hasher
, concat
, challenge_hash
))
316 hasher
->destroy(hasher
);
319 hasher
->destroy(hasher
);
320 /* we need only the first 8 octets */
321 challenge_hash
->len
= 8;
326 * Calculate the challenge response (i.e. expand password_hash to three DES keys
327 * and then encrypt the 8-octet challenge_hash with these keys and concatenate
330 static status_t
ChallengeResponse(chunk_t challenge_hash
, chunk_t password_hash
,
335 chunk_t keys
[3], z_password_hash
;
336 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, ENCR_DES_ECB
, 8);
339 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
342 /* prepare keys: first pad password_hash to 21 octets, these get then split
343 * into 7-octet chunks, which then get expanded into 8-octet DES keys */
344 z_password_hash
= chunk_alloca(21);
345 memset(z_password_hash
.ptr
, 0, z_password_hash
.len
);
346 memcpy(z_password_hash
.ptr
, password_hash
.ptr
, password_hash
.len
);
347 chunk_split(z_password_hash
, "mmm", 7, &keys
[0], 7, &keys
[1], 7, &keys
[2]);
349 *response
= chunk_alloc(24);
350 for (i
= 0; i
< 3; i
++)
352 chunk_t expanded
, encrypted
;
354 expanded
= ExpandDESKey(keys
[i
]);
355 if (!crypter
->set_key(crypter
, expanded
) ||
356 !crypter
->encrypt(crypter
, challenge_hash
, chunk_empty
, &encrypted
))
358 chunk_clear(&expanded
);
359 crypter
->destroy(crypter
);
362 memcpy(&response
->ptr
[i
* 8], encrypted
.ptr
, encrypted
.len
);
363 chunk_clear(&encrypted
);
364 chunk_clear(&expanded
);
366 crypter
->destroy(crypter
);
371 * Computes the authenticator response
373 static status_t
AuthenticatorResponse(chunk_t password_hash_hash
,
374 chunk_t challenge_hash
, chunk_t nt_response
, chunk_t
*response
)
376 chunk_t magic1
= chunk_from_chars(
377 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
378 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
379 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
380 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
381 chunk_t magic2
= chunk_from_chars(
382 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
383 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
384 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
385 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
387 chunk_t digest
= chunk_empty
, concat
;
390 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
393 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
397 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
398 if (!hasher
->allocate_hash(hasher
, concat
, &digest
))
400 hasher
->destroy(hasher
);
403 concat
= chunk_cata("ccc", digest
, challenge_hash
, magic2
);
404 if (!hasher
->allocate_hash(hasher
, concat
, response
))
406 hasher
->destroy(hasher
);
409 hasher
->destroy(hasher
);
415 * Generate the master session key according to RFC3079
417 static status_t
GenerateMSK(chunk_t password_hash_hash
,
418 chunk_t nt_response
, chunk_t
*msk
)
420 chunk_t magic1
= chunk_from_chars(
421 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
422 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
423 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
424 chunk_t magic2
= chunk_from_chars(
425 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
426 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
427 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
428 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
429 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
430 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
431 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
432 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
433 0x6b, 0x65, 0x79, 0x2e);
434 chunk_t magic3
= chunk_from_chars(
435 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
436 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
437 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
438 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
439 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
440 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
441 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
442 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
443 0x6b, 0x65, 0x79, 0x2e);
444 chunk_t shapad1
= chunk_from_chars(
445 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
447 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
449 chunk_t shapad2
= chunk_from_chars(
450 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
451 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
452 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
453 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);
454 chunk_t keypad
= chunk_from_chars(
455 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
457 char master_key
[HASH_SIZE_SHA1
];
458 char master_receive_key
[HASH_SIZE_SHA1
], master_send_key
[HASH_SIZE_SHA1
];
459 chunk_t concat
, master
;
462 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
465 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
469 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
470 if (!hasher
->get_hash(hasher
, concat
, master_key
))
472 hasher
->destroy(hasher
);
475 master
= chunk_create(master_key
, 16);
476 concat
= chunk_cata("cccc", master
, shapad1
, magic2
, shapad2
);
477 if (!hasher
->get_hash(hasher
, concat
, master_receive_key
))
479 hasher
->destroy(hasher
);
482 concat
= chunk_cata("cccc", master
, shapad1
, magic3
, shapad2
);
483 if (!hasher
->get_hash(hasher
, concat
, master_send_key
))
485 hasher
->destroy(hasher
);
489 *msk
= chunk_cat("cccc", chunk_create(master_receive_key
, 16),
490 chunk_create(master_send_key
, 16), keypad
, keypad
);
492 hasher
->destroy(hasher
);
496 static status_t
GenerateStuff(private_eap_mschapv2_t
*this,
497 chunk_t server_challenge
, chunk_t peer_challenge
,
498 chunk_t username
, chunk_t nt_hash
)
500 status_t status
= FAILED
;
501 chunk_t nt_hash_hash
= chunk_empty
, challenge_hash
= chunk_empty
;
503 if (NtPasswordHash(nt_hash
, &nt_hash_hash
) != SUCCESS
)
507 if (ChallengeHash(peer_challenge
, server_challenge
, username
,
508 &challenge_hash
) != SUCCESS
)
512 if (ChallengeResponse(challenge_hash
, nt_hash
,
513 &this->nt_response
) != SUCCESS
)
517 if (AuthenticatorResponse(nt_hash_hash
, challenge_hash
,
518 this->nt_response
, &this->auth_response
) != SUCCESS
)
522 if (GenerateMSK(nt_hash_hash
, this->nt_response
, &this->msk
) != SUCCESS
)
530 chunk_free(&nt_hash_hash
);
531 chunk_free(&challenge_hash
);
536 * Converts an ASCII string into a UTF-16 (little-endian) string
538 static chunk_t
ascii_to_unicode(chunk_t ascii
)
541 chunk_t unicode
= chunk_alloc(ascii
.len
* 2);
542 for (i
= 0; i
< ascii
.len
; i
++)
544 unicode
.ptr
[i
* 2] = ascii
.ptr
[i
];
545 unicode
.ptr
[i
* 2 + 1] = 0;
551 * sanitize a string for printing
553 static char* sanitize(char *str
)
569 * Returns a chunk of just the username part of the given user identity.
570 * Note: the chunk points to internal data of the given chunk
572 static chunk_t
extract_username(chunk_t id
)
576 has_domain
= (char*)memchr(id
.ptr
, '\\', id
.len
);
580 has_domain
++; /* skip the backslash */
581 len
= id
.len
- ((u_char
*)has_domain
- id
.ptr
);
582 return len
> 0 ?
chunk_create(has_domain
, len
) : chunk_empty
;
588 * Set the ms_length field using aligned write
590 static void set_ms_length(eap_mschapv2_header_t
*eap
, u_int16_t len
)
592 len
= htons(len
- 5);
593 memcpy(&eap
->ms_length
, &len
, sizeof(u_int16_t
));
596 METHOD(eap_method_t
, initiate_peer
, status_t
,
597 private_eap_mschapv2_t
*this, eap_payload_t
**out
)
599 /* peer never initiates */
603 METHOD(eap_method_t
, initiate_server
, status_t
,
604 private_eap_mschapv2_t
*this, eap_payload_t
**out
)
607 eap_mschapv2_header_t
*eap
;
608 eap_mschapv2_challenge_t
*cha
;
609 const char *name
= MSCHAPV2_HOST_NAME
;
610 u_int16_t len
= CHALLENGE_PAYLOAD_LEN
+ sizeof(MSCHAPV2_HOST_NAME
) - 1;
612 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
613 if (!rng
|| !rng
->allocate_bytes(rng
, CHALLENGE_LEN
, &this->challenge
))
615 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no challenge");
622 eap
->code
= EAP_REQUEST
;
623 eap
->identifier
= this->identifier
;
624 eap
->length
= htons(len
);
625 eap
->type
= EAP_MSCHAPV2
;
626 eap
->opcode
= MSCHAPV2_CHALLENGE
;
627 eap
->ms_chapv2_id
= this->mschapv2id
;
628 set_ms_length(eap
, len
);
630 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
631 cha
->value_size
= CHALLENGE_LEN
;
632 memcpy(cha
->challenge
, this->challenge
.ptr
, this->challenge
.len
);
633 memcpy(cha
->name
, name
, sizeof(MSCHAPV2_HOST_NAME
) - 1);
635 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
639 static bool get_nt_hash(private_eap_mschapv2_t
*this, identification_t
*me
,
640 identification_t
*other
, chunk_t
*nt_hash
)
642 shared_key_t
*shared
;
645 /* try to find a stored NT_HASH first */
646 shared
= lib
->credmgr
->get_shared(lib
->credmgr
, SHARED_NT_HASH
, me
, other
);
649 *nt_hash
= chunk_clone(shared
->get_key(shared
));
650 shared
->destroy(shared
);
654 /* fallback to plaintext password */
655 shared
= lib
->credmgr
->get_shared(lib
->credmgr
, SHARED_EAP
, me
, other
);
658 password
= ascii_to_unicode(shared
->get_key(shared
));
659 shared
->destroy(shared
);
661 if (NtPasswordHash(password
, nt_hash
) == SUCCESS
)
663 chunk_clear(&password
);
666 chunk_clear(&password
);
672 * Process MS-CHAPv2 Challenge Requests
674 static status_t
process_peer_challenge(private_eap_mschapv2_t
*this,
675 eap_payload_t
*in
, eap_payload_t
**out
)
678 eap_mschapv2_header_t
*eap
;
679 eap_mschapv2_challenge_t
*cha
;
680 eap_mschapv2_response_t
*res
;
681 chunk_t data
, peer_challenge
, userid
, username
, nt_hash
;
682 u_int16_t len
= RESPONSE_PAYLOAD_LEN
;
684 data
= in
->get_data(in
);
685 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
687 /* the name MUST be at least one octet long */
688 if (data
.len
< CHALLENGE_PAYLOAD_LEN
+ 1)
690 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
694 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
696 if (cha
->value_size
!= CHALLENGE_LEN
)
698 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
699 "invalid challenge size");
703 this->mschapv2id
= eap
->ms_chapv2_id
;
704 this->challenge
= chunk_clone(chunk_create(cha
->challenge
, CHALLENGE_LEN
));
706 peer_challenge
= chunk_alloca(CHALLENGE_LEN
);
707 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
708 if (!rng
|| !rng
->get_bytes(rng
, CHALLENGE_LEN
, peer_challenge
.ptr
))
710 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, allocating challenge failed");
716 if (!get_nt_hash(this, this->peer
, this->server
, &nt_hash
))
718 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
719 this->server
, this->peer
);
723 /* we transmit the whole user identity (including the domain part) but
724 * only use the user part when calculating the challenge hash */
725 userid
= this->peer
->get_encoding(this->peer
);
727 username
= extract_username(userid
);
729 if (GenerateStuff(this, this->challenge
, peer_challenge
,
730 username
, nt_hash
) != SUCCESS
)
732 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 generating NT-Response failed");
733 chunk_clear(&nt_hash
);
736 chunk_clear(&nt_hash
);
739 eap
->code
= EAP_RESPONSE
;
740 eap
->identifier
= this->identifier
;
741 eap
->length
= htons(len
);
742 eap
->type
= EAP_MSCHAPV2
;
743 eap
->opcode
= MSCHAPV2_RESPONSE
;
744 eap
->ms_chapv2_id
= this->mschapv2id
;
745 set_ms_length(eap
, len
);
747 res
= (eap_mschapv2_response_t
*)eap
->data
;
748 res
->value_size
= RESPONSE_LEN
;
749 memset(&res
->response
, 0, RESPONSE_LEN
);
750 memcpy(res
->response
.peer_challenge
, peer_challenge
.ptr
, peer_challenge
.len
);
751 memcpy(res
->response
.nt_response
, this->nt_response
.ptr
, this->nt_response
.len
);
752 memcpy(res
->name
, userid
.ptr
, userid
.len
);
754 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
759 * Process MS-CHAPv2 Success Requests
761 static status_t
process_peer_success(private_eap_mschapv2_t
*this,
762 eap_payload_t
*in
, eap_payload_t
**out
)
764 status_t status
= FAILED
;
765 enumerator_t
*enumerator
;
766 eap_mschapv2_header_t
*eap
;
767 chunk_t data
, auth_string
= chunk_empty
;
768 char *message
, *token
, *msg
= NULL
;
770 u_int16_t len
= SHORT_HEADER_LEN
;
772 data
= in
->get_data(in
);
773 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
775 if (data
.len
< AUTH_RESPONSE_LEN
)
777 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
781 message_len
= data
.len
- HEADER_LEN
;
782 message
= malloc(message_len
+ 1);
783 memcpy(message
, eap
->data
, message_len
);
784 message
[message_len
] = '\0';
786 /* S=<auth_string> M=<msg> */
787 enumerator
= enumerator_create_token(message
, " ", " ");
788 while (enumerator
->enumerate(enumerator
, &token
))
790 if (strpfx(token
, "S="))
794 if (strlen(token
) != AUTH_RESPONSE_LEN
- 2)
796 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
797 "invalid auth string");
800 chunk_free(&auth_string
);
801 hex
= chunk_create(token
, AUTH_RESPONSE_LEN
- 2);
802 auth_string
= chunk_from_hex(hex
, NULL
);
804 else if (strpfx(token
, "M="))
811 enumerator
->destroy(enumerator
);
813 if (auth_string
.ptr
== NULL
)
815 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
816 "auth string missing");
820 if (!chunk_equals_const(this->auth_response
, auth_string
))
822 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
826 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg
));
829 eap
->code
= EAP_RESPONSE
;
830 eap
->identifier
= this->identifier
;
831 eap
->length
= htons(len
);
832 eap
->type
= EAP_MSCHAPV2
;
833 eap
->opcode
= MSCHAPV2_SUCCESS
;
835 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
839 chunk_free(&auth_string
);
845 static status_t
process_peer_failure(private_eap_mschapv2_t
*this,
846 eap_payload_t
*in
, eap_payload_t
**out
)
848 status_t status
= FAILED
;
849 enumerator_t
*enumerator
;
850 eap_mschapv2_header_t
*eap
;
852 char *message
, *token
, *msg
= NULL
;
853 int message_len
, error
= 0;
854 chunk_t challenge
= chunk_empty
;
856 data
= in
->get_data(in
);
857 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
859 if (data
.len
< 3) /* we want at least an error code: E=e */
861 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
865 message_len
= data
.len
- HEADER_LEN
;
866 message
= malloc(message_len
+ 1);
867 memcpy(message
, eap
->data
, message_len
);
868 message
[message_len
] = '\0';
870 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
871 enumerator
= enumerator_create_token(message
, " ", " ");
872 while (enumerator
->enumerate(enumerator
, &token
))
874 if (strpfx(token
, "E="))
879 else if (strpfx(token
, "R="))
881 /* ignore retriable */
883 else if (strpfx(token
, "C="))
887 if (strlen(token
) != 2 * CHALLENGE_LEN
)
889 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message:"
890 "invalid challenge");
893 chunk_free(&challenge
);
894 hex
= chunk_create(token
, 2 * CHALLENGE_LEN
);
895 challenge
= chunk_from_hex(hex
, NULL
);
897 else if (strpfx(token
, "V="))
901 else if (strpfx(token
, "M="))
908 enumerator
->destroy(enumerator
);
910 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed with error %N: '%s'",
911 mschapv2_error_names
, error
, sanitize(msg
));
914 * at this point, if the error is retriable, we MAY retry the authentication
915 * or MAY send a Change Password packet.
917 * if the error is not retriable (or if we do neither of the above), we
918 * SHOULD send a Failure Response packet.
919 * windows clients don't do that, and since windows server 2008 r2 behaves
920 * pretty odd if we do send a Failure Response, we just don't send one
921 * either. windows 7 actually sends a delete notify (which, according to the
922 * logs, results in an error on windows server 2008 r2).
924 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
925 * a disabled account but returns the windows error code in a notify payload
932 chunk_free(&challenge
);
938 METHOD(eap_method_t
, process_peer
, status_t
,
939 private_eap_mschapv2_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
942 eap_mschapv2_header_t
*eap
;
944 this->identifier
= in
->get_identifier(in
);
945 data
= in
->get_data(in
);
946 if (data
.len
< SHORT_HEADER_LEN
)
948 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message");
952 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
956 case MSCHAPV2_CHALLENGE
:
958 return process_peer_challenge(this, in
, out
);
960 case MSCHAPV2_SUCCESS
:
962 return process_peer_success(this, in
, out
);
964 case MSCHAPV2_FAILURE
:
966 return process_peer_failure(this, in
, out
);
970 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
971 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
979 * Handles retries on the server
981 static status_t
process_server_retry(private_eap_mschapv2_t
*this,
984 eap_mschapv2_header_t
*eap
;
987 char msg
[FAILURE_MESSAGE_LEN
];
988 u_int16_t len
= HEADER_LEN
+ FAILURE_MESSAGE_LEN
- 1; /* no null byte */
990 if (++this->retries
> MAX_RETRIES
)
992 /* we MAY send a Failure Request with R=0, but windows 7 does not
993 * really like that and does not respond with a Failure Response.
994 * so, to clean up our state we just fail with an EAP-Failure.
995 * this gives an unknown error on the windows side, but is also fine
996 * with the standard. */
997 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed: "
998 "maximum number of retries reached");
1002 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries
);
1004 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
1005 if (!rng
|| !rng
->get_bytes(rng
, CHALLENGE_LEN
, this->challenge
.ptr
))
1007 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, allocating challenge failed");
1013 chunk_free(&this->nt_response
);
1014 chunk_free(&this->auth_response
);
1015 chunk_free(&this->msk
);
1018 eap
->code
= EAP_REQUEST
;
1019 eap
->identifier
= ++this->identifier
;
1020 eap
->length
= htons(len
);
1021 eap
->type
= EAP_MSCHAPV2
;
1022 eap
->opcode
= MSCHAPV2_FAILURE
;
1023 eap
->ms_chapv2_id
= this->mschapv2id
++; /* increase for each retry */
1024 set_ms_length(eap
, len
);
1026 hex
= chunk_to_hex(this->challenge
, NULL
, TRUE
);
1027 snprintf(msg
, FAILURE_MESSAGE_LEN
, "%s%s", FAILURE_MESSAGE
, hex
.ptr
);
1029 memcpy(eap
->data
, msg
, FAILURE_MESSAGE_LEN
- 1); /* no null byte */
1030 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1032 /* delay the response for some time to make brute-force attacks harder */
1039 * Process MS-CHAPv2 Response response packets
1041 static status_t
process_server_response(private_eap_mschapv2_t
*this,
1042 eap_payload_t
*in
, eap_payload_t
**out
)
1044 eap_mschapv2_header_t
*eap
;
1045 eap_mschapv2_response_t
*res
;
1046 chunk_t data
, peer_challenge
, username
, nt_hash
;
1047 identification_t
*userid
;
1051 data
= in
->get_data(in
);
1052 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1054 if (data
.len
< RESPONSE_PAYLOAD_LEN
)
1056 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1060 res
= (eap_mschapv2_response_t
*)eap
->data
;
1061 peer_challenge
= chunk_create(res
->response
.peer_challenge
, CHALLENGE_LEN
);
1063 name_len
= min(data
.len
- RESPONSE_PAYLOAD_LEN
, 255);
1064 snprintf(buf
, sizeof(buf
), "%.*s", name_len
, res
->name
);
1065 userid
= identification_create_from_string(buf
);
1066 DBG2(DBG_IKE
, "EAP-MS-CHAPv2 username: '%Y'", userid
);
1067 /* userid can only be destroyed after the last use of username */
1068 username
= extract_username(userid
->get_encoding(userid
));
1070 if (!get_nt_hash(this, this->server
, userid
, &nt_hash
))
1072 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
1073 this->server
, userid
);
1074 /* FIXME: windows 7 always sends the username that is first entered in
1075 * the username box, even, if the user changes it during retries (probably
1076 * to keep consistent with the EAP-Identity).
1077 * thus, we could actually fail here, because retries do not make much
1078 * sense. on the other hand, an attacker could guess usernames, if the
1079 * error messages were different. */
1080 userid
->destroy(userid
);
1081 return process_server_retry(this, out
);
1084 if (GenerateStuff(this, this->challenge
, peer_challenge
,
1085 username
, nt_hash
) != SUCCESS
)
1087 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
1088 userid
->destroy(userid
);
1089 chunk_clear(&nt_hash
);
1092 chunk_clear(&nt_hash
);
1094 if (memeq_const(res
->response
.nt_response
, this->nt_response
.ptr
,
1095 this->nt_response
.len
))
1098 char msg
[AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
)];
1099 u_int16_t len
= HEADER_LEN
+ AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
);
1102 eap
->code
= EAP_REQUEST
;
1103 eap
->identifier
= ++this->identifier
;
1104 eap
->length
= htons(len
);
1105 eap
->type
= EAP_MSCHAPV2
;
1106 eap
->opcode
= MSCHAPV2_SUCCESS
;
1107 eap
->ms_chapv2_id
= this->mschapv2id
;
1108 set_ms_length(eap
, len
);
1110 hex
= chunk_to_hex(this->auth_response
, NULL
, TRUE
);
1111 snprintf(msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
),
1112 "S=%s%s", hex
.ptr
, SUCCESS_MESSAGE
);
1114 memcpy(eap
->data
, msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
));
1115 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1117 this->auth
->add(this->auth
, AUTH_RULE_EAP_IDENTITY
, userid
);
1120 userid
->destroy(userid
);
1121 return process_server_retry(this, out
);
1124 METHOD(eap_method_t
, process_server
, status_t
,
1125 private_eap_mschapv2_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
1127 eap_mschapv2_header_t
*eap
;
1130 if (this->identifier
!= in
->get_identifier(in
))
1132 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
1133 "unexpected identifier");
1137 data
= in
->get_data(in
);
1138 if (data
.len
< SHORT_HEADER_LEN
)
1140 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1144 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1146 switch (eap
->opcode
)
1148 case MSCHAPV2_RESPONSE
:
1150 return process_server_response(this, in
, out
);
1152 case MSCHAPV2_SUCCESS
:
1156 case MSCHAPV2_FAILURE
:
1162 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
1163 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
1170 METHOD(eap_method_t
, get_type
, eap_type_t
,
1171 private_eap_mschapv2_t
*this, u_int32_t
*vendor
)
1174 return EAP_MSCHAPV2
;
1177 METHOD(eap_method_t
, get_msk
, status_t
,
1178 private_eap_mschapv2_t
*this, chunk_t
*msk
)
1188 METHOD(eap_method_t
, get_identifier
, u_int8_t
,
1189 private_eap_mschapv2_t
*this)
1191 return this->identifier
;
1194 METHOD(eap_method_t
, set_identifier
, void,
1195 private_eap_mschapv2_t
*this, u_int8_t identifier
)
1197 this->identifier
= identifier
;
1200 METHOD(eap_method_t
, is_mutual
, bool,
1201 private_eap_mschapv2_t
*this)
1206 METHOD(eap_method_t
, get_auth
, auth_cfg_t
*,
1207 private_eap_mschapv2_t
*this)
1212 METHOD(eap_method_t
, destroy
, void,
1213 private_eap_mschapv2_t
*this)
1215 this->peer
->destroy(this->peer
);
1216 this->server
->destroy(this->server
);
1217 this->auth
->destroy(this->auth
);
1218 chunk_free(&this->challenge
);
1219 chunk_free(&this->nt_response
);
1220 chunk_free(&this->auth_response
);
1221 chunk_free(&this->msk
);
1226 * Generic constructor
1228 static private_eap_mschapv2_t
*eap_mschapv2_create_generic(identification_t
*server
, identification_t
*peer
)
1230 private_eap_mschapv2_t
*this;
1234 .eap_method_interface
= {
1235 .get_type
= _get_type
,
1236 .is_mutual
= _is_mutual
,
1237 .get_msk
= _get_msk
,
1238 .get_identifier
= _get_identifier
,
1239 .set_identifier
= _set_identifier
,
1240 .get_auth
= _get_auth
,
1241 .destroy
= _destroy
,
1244 .peer
= peer
->clone(peer
),
1245 .server
= server
->clone(server
),
1246 .auth
= auth_cfg_create(),
1255 eap_mschapv2_t
*eap_mschapv2_create_server(identification_t
*server
, identification_t
*peer
)
1257 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
1259 this->public.eap_method_interface
.initiate
= _initiate_server
;
1260 this->public.eap_method_interface
.process
= _process_server
;
1262 /* generate a non-zero identifier */
1265 this->identifier
= random();
1266 } while (!this->identifier
);
1268 this->mschapv2id
= this->identifier
;
1270 return &this->public;
1276 eap_mschapv2_t
*eap_mschapv2_create_peer(identification_t
*server
, identification_t
*peer
)
1278 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
1280 this->public.eap_method_interface
.initiate
= _initiate_peer
;
1281 this->public.eap_method_interface
.process
= _process_peer
;
1283 return &this->public;