2 * Copyright (C) 2009-2015 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
104 enum mschapv2_opcode_t
106 MSCHAPV2_CHALLENGE
= 1,
107 MSCHAPV2_RESPONSE
= 2,
108 MSCHAPV2_SUCCESS
= 3,
109 MSCHAPV2_FAILURE
= 4,
110 MSCHAPV2_CHANGE_PASSWORD
= 7,
116 ENUM_BEGIN(mschapv2_opcode_names
, MSCHAPV2_CHALLENGE
, MSCHAPV2_FAILURE
,
121 ENUM_NEXT(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_CHANGE_PASSWORD
, MSCHAPV2_FAILURE
,
123 ENUM_END(mschapv2_opcode_names
, MSCHAPV2_CHANGE_PASSWORD
);
128 enum mschapv2_error_t
130 ERROR_RESTRICTED_LOGON_HOURS
= 646,
131 ERROR_ACCT_DISABLED
= 647,
132 ERROR_PASSWD_EXPIRED
= 648,
133 ERROR_NO_DIALIN_PERMISSION
= 649,
134 ERROR_AUTHENTICATION_FAILURE
= 691,
135 ERROR_CHANGING_PASSWORD
= 709,
139 * Names for error codes
141 ENUM_BEGIN(mschapv2_error_names
, ERROR_RESTRICTED_LOGON_HOURS
, ERROR_NO_DIALIN_PERMISSION
,
142 "ERROR_RESTRICTED_LOGON_HOURS",
143 "ERROR_ACCT_DISABLED",
144 "ERROR_PASSWD_EXPIRED",
145 "ERROR_NO_DIALIN_PERMISSION");
146 ENUM_NEXT(mschapv2_error_names
, ERROR_AUTHENTICATION_FAILURE
, ERROR_AUTHENTICATION_FAILURE
, ERROR_NO_DIALIN_PERMISSION
,
147 "ERROR_AUTHENTICATION_FAILURE");
148 ENUM_NEXT(mschapv2_error_names
, ERROR_CHANGING_PASSWORD
, ERROR_CHANGING_PASSWORD
, ERROR_AUTHENTICATION_FAILURE
,
149 "ERROR_CHANGING_PASSWORD");
150 ENUM_END(mschapv2_error_names
, ERROR_CHANGING_PASSWORD
);
152 /* Length of the challenge */
153 #define CHALLENGE_LEN 16
154 /* Length of the response (see eap_mschapv2_response_t) */
155 #define RESPONSE_LEN 49
156 /* Length of the authenticator response string ("S=<...>") */
157 #define AUTH_RESPONSE_LEN 42
158 /* Name we send as authenticator */
159 #define MSCHAPV2_HOST_NAME "strongSwan"
160 /* Message sent on success */
161 #define SUCCESS_MESSAGE " M=Welcome2strongSwan"
162 /* Message sent on failure */
163 #define FAILURE_MESSAGE "E=691 R=1 C="
164 /* Length of the complete failure message */
165 #define FAILURE_MESSAGE_LEN (sizeof(FAILURE_MESSAGE) + CHALLENGE_LEN * 2)
167 /* Number of seconds to delay retries */
168 #define RETRY_DELAY 2
169 /* Maximum number of retries */
170 #define MAX_RETRIES 2
172 typedef struct eap_mschapv2_header_t eap_mschapv2_header_t
;
173 typedef struct eap_mschapv2_challenge_t eap_mschapv2_challenge_t
;
174 typedef struct eap_mschapv2_response_t eap_mschapv2_response_t
;
177 * packed EAP-MS-CHAPv2 header struct
179 struct eap_mschapv2_header_t
181 /** EAP code (REQUEST/RESPONSE) */
183 /** unique message identifier */
185 /** length of whole message */
189 /** MS-CHAPv2 OpCode */
191 /** MS-CHAPv2-ID (equals identifier) */
192 u_int8_t ms_chapv2_id
;
193 /** MS-Length (defined as length - 5) */
195 /** packet data (determined by OpCode) */
197 }__attribute__((__packed__
));
200 * packed data for a MS-CHAPv2 Challenge packet
202 struct eap_mschapv2_challenge_t
207 u_int8_t challenge
[CHALLENGE_LEN
];
210 }__attribute__((__packed__
));
213 * packed data for a MS-CHAPv2 Response packet
215 struct eap_mschapv2_response_t
223 u_int8_t peer_challenge
[CHALLENGE_LEN
];
224 /* Reserved (=zero) */
225 u_int8_t peer_reserved
[8];
227 u_int8_t nt_response
[24];
233 }__attribute__((__packed__
));
236 * Length of the MS-CHAPv2 header
238 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
241 * Length of the header for MS-CHAPv2 success/failure packets (does not include
242 * MS-CHAPv2-ID and MS-Length, i.e. 3 octets)
244 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
247 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
248 * at least one octet)
250 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
253 * The minimum length of an MS-CHAPv2 Response packet
255 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
259 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
261 static chunk_t
ExpandDESKey(chunk_t key
)
263 static const u_char bitmask
[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
268 /* expand the 7 octets to 8 octets */
269 expanded
= chunk_alloc(8);
270 for (i
= 0; i
< 7; i
++)
272 expanded
.ptr
[i
] = ((key
.ptr
[i
] & bitmask
[i
]) >> i
) | (carry
<< (8 - i
));
273 carry
= key
.ptr
[i
] & ~bitmask
[i
];
275 expanded
.ptr
[7] = carry
<< 1;
277 /* add parity bits to each octet */
278 for (i
= 0; i
< 8; i
++)
280 u_char val
= expanded
.ptr
[i
];
281 val
= (val
^ (val
>> 4)) & 0x0f;
282 expanded
.ptr
[i
] |= (0x9669 >> val
) & 1;
288 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
290 static status_t
NtPasswordHash(chunk_t password
, chunk_t
*password_hash
)
293 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD4
);
296 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
299 if (!hasher
->allocate_hash(hasher
, password
, password_hash
))
301 hasher
->destroy(hasher
);
304 hasher
->destroy(hasher
);
309 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
310 * username (without domain part)] with SHA1)
312 static status_t
ChallengeHash(chunk_t peer_challenge
, chunk_t server_challenge
,
313 chunk_t username
, chunk_t
*challenge_hash
)
317 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
320 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
323 concat
= chunk_cata("ccc", peer_challenge
, server_challenge
, username
);
324 if (!hasher
->allocate_hash(hasher
, concat
, challenge_hash
))
326 hasher
->destroy(hasher
);
329 hasher
->destroy(hasher
);
330 /* we need only the first 8 octets */
331 challenge_hash
->len
= 8;
336 * Calculate the challenge response (i.e. expand password_hash to three DES keys
337 * and then encrypt the 8-octet challenge_hash with these keys and concatenate
340 static status_t
ChallengeResponse(chunk_t challenge_hash
, chunk_t password_hash
,
345 chunk_t keys
[3], z_password_hash
;
346 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, ENCR_DES_ECB
, 8);
349 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
352 /* prepare keys: first pad password_hash to 21 octets, these get then split
353 * into 7-octet chunks, which then get expanded into 8-octet DES keys */
354 z_password_hash
= chunk_alloca(21);
355 memset(z_password_hash
.ptr
, 0, z_password_hash
.len
);
356 memcpy(z_password_hash
.ptr
, password_hash
.ptr
, password_hash
.len
);
357 chunk_split(z_password_hash
, "mmm", 7, &keys
[0], 7, &keys
[1], 7, &keys
[2]);
359 *response
= chunk_alloc(24);
360 for (i
= 0; i
< 3; i
++)
362 chunk_t expanded
, encrypted
;
364 expanded
= ExpandDESKey(keys
[i
]);
365 if (!crypter
->set_key(crypter
, expanded
) ||
366 !crypter
->encrypt(crypter
, challenge_hash
, chunk_empty
, &encrypted
))
368 chunk_clear(&expanded
);
369 crypter
->destroy(crypter
);
372 memcpy(&response
->ptr
[i
* 8], encrypted
.ptr
, encrypted
.len
);
373 chunk_clear(&encrypted
);
374 chunk_clear(&expanded
);
376 crypter
->destroy(crypter
);
381 * Computes the authenticator response
383 static status_t
AuthenticatorResponse(chunk_t password_hash_hash
,
384 chunk_t challenge_hash
, chunk_t nt_response
, chunk_t
*response
)
386 chunk_t magic1
= chunk_from_chars(
387 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
388 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
389 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
390 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
391 chunk_t magic2
= chunk_from_chars(
392 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
393 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
394 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
395 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
397 chunk_t digest
= chunk_empty
, concat
;
400 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
403 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
407 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
408 if (!hasher
->allocate_hash(hasher
, concat
, &digest
))
410 hasher
->destroy(hasher
);
413 concat
= chunk_cata("ccc", digest
, challenge_hash
, magic2
);
414 if (!hasher
->allocate_hash(hasher
, concat
, response
))
416 hasher
->destroy(hasher
);
419 hasher
->destroy(hasher
);
425 * Generate the master session key according to RFC3079
427 static status_t
GenerateMSK(chunk_t password_hash_hash
,
428 chunk_t nt_response
, chunk_t
*msk
)
430 chunk_t magic1
= chunk_from_chars(
431 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
432 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
433 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
434 chunk_t magic2
= 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, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
439 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
440 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
441 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
442 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
443 0x6b, 0x65, 0x79, 0x2e);
444 chunk_t magic3
= chunk_from_chars(
445 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
446 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
447 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
448 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
449 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
450 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
451 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
452 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
453 0x6b, 0x65, 0x79, 0x2e);
454 chunk_t shapad1
= chunk_from_chars(
455 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
459 chunk_t shapad2
= chunk_from_chars(
460 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
461 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
462 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
463 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);
464 chunk_t keypad
= chunk_from_chars(
465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
467 char master_key
[HASH_SIZE_SHA1
];
468 char master_receive_key
[HASH_SIZE_SHA1
], master_send_key
[HASH_SIZE_SHA1
];
469 chunk_t concat
, master
;
472 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_SHA1
);
475 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, SHA1 not supported");
479 concat
= chunk_cata("ccc", password_hash_hash
, nt_response
, magic1
);
480 if (!hasher
->get_hash(hasher
, concat
, master_key
))
482 hasher
->destroy(hasher
);
485 master
= chunk_create(master_key
, 16);
486 concat
= chunk_cata("cccc", master
, shapad1
, magic2
, shapad2
);
487 if (!hasher
->get_hash(hasher
, concat
, master_receive_key
))
489 hasher
->destroy(hasher
);
492 concat
= chunk_cata("cccc", master
, shapad1
, magic3
, shapad2
);
493 if (!hasher
->get_hash(hasher
, concat
, master_send_key
))
495 hasher
->destroy(hasher
);
499 *msk
= chunk_cat("cccc", chunk_create(master_receive_key
, 16),
500 chunk_create(master_send_key
, 16), keypad
, keypad
);
502 hasher
->destroy(hasher
);
506 static status_t
GenerateStuff(private_eap_mschapv2_t
*this,
507 chunk_t server_challenge
, chunk_t peer_challenge
,
508 chunk_t username
, chunk_t nt_hash
)
510 status_t status
= FAILED
;
511 chunk_t nt_hash_hash
= chunk_empty
, challenge_hash
= chunk_empty
;
513 if (NtPasswordHash(nt_hash
, &nt_hash_hash
) != SUCCESS
)
517 if (ChallengeHash(peer_challenge
, server_challenge
, username
,
518 &challenge_hash
) != SUCCESS
)
522 if (ChallengeResponse(challenge_hash
, nt_hash
,
523 &this->nt_response
) != SUCCESS
)
527 if (AuthenticatorResponse(nt_hash_hash
, challenge_hash
,
528 this->nt_response
, &this->auth_response
) != SUCCESS
)
532 if (GenerateMSK(nt_hash_hash
, this->nt_response
, &this->msk
) != SUCCESS
)
540 chunk_free(&nt_hash_hash
);
541 chunk_free(&challenge_hash
);
546 * Converts an ASCII string into a UTF-16 (little-endian) string
548 static chunk_t
ascii_to_unicode(chunk_t ascii
)
551 chunk_t unicode
= chunk_alloc(ascii
.len
* 2);
552 for (i
= 0; i
< ascii
.len
; i
++)
554 unicode
.ptr
[i
* 2] = ascii
.ptr
[i
];
555 unicode
.ptr
[i
* 2 + 1] = 0;
561 * sanitize a string for printing
563 static char* sanitize(char *str
)
579 * Returns a chunk of just the username part of the given user identity.
580 * Note: the chunk points to internal data of the given chunk
582 static chunk_t
extract_username(chunk_t id
)
586 has_domain
= (char*)memchr(id
.ptr
, '\\', id
.len
);
590 has_domain
++; /* skip the backslash */
591 len
= id
.len
- ((u_char
*)has_domain
- id
.ptr
);
592 return len
> 0 ?
chunk_create(has_domain
, len
) : chunk_empty
;
598 * Set the ms_length field using aligned write
600 static void set_ms_length(eap_mschapv2_header_t
*eap
, u_int16_t len
)
602 len
= htons(len
- 5);
603 memcpy(&eap
->ms_length
, &len
, sizeof(u_int16_t
));
606 METHOD(eap_method_t
, initiate_peer
, status_t
,
607 private_eap_mschapv2_t
*this, eap_payload_t
**out
)
609 /* peer never initiates */
613 METHOD(eap_method_t
, initiate_server
, status_t
,
614 private_eap_mschapv2_t
*this, eap_payload_t
**out
)
617 eap_mschapv2_header_t
*eap
;
618 eap_mschapv2_challenge_t
*cha
;
619 const char *name
= MSCHAPV2_HOST_NAME
;
620 u_int16_t len
= CHALLENGE_PAYLOAD_LEN
+ sizeof(MSCHAPV2_HOST_NAME
) - 1;
622 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
623 if (!rng
|| !rng
->allocate_bytes(rng
, CHALLENGE_LEN
, &this->challenge
))
625 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, no challenge");
632 eap
->code
= EAP_REQUEST
;
633 eap
->identifier
= this->identifier
;
634 eap
->length
= htons(len
);
635 eap
->type
= EAP_MSCHAPV2
;
636 eap
->opcode
= MSCHAPV2_CHALLENGE
;
637 eap
->ms_chapv2_id
= this->mschapv2id
;
638 set_ms_length(eap
, len
);
640 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
641 cha
->value_size
= CHALLENGE_LEN
;
642 memcpy(cha
->challenge
, this->challenge
.ptr
, this->challenge
.len
);
643 memcpy(cha
->name
, name
, sizeof(MSCHAPV2_HOST_NAME
) - 1);
645 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
646 this->state
= S_EXPECT_RESPONSE
;
650 static bool get_nt_hash(private_eap_mschapv2_t
*this, identification_t
*me
,
651 identification_t
*other
, chunk_t
*nt_hash
)
653 shared_key_t
*shared
;
656 /* try to find a stored NT_HASH first */
657 shared
= lib
->credmgr
->get_shared(lib
->credmgr
, SHARED_NT_HASH
, me
, other
);
660 *nt_hash
= chunk_clone(shared
->get_key(shared
));
661 shared
->destroy(shared
);
665 /* fallback to plaintext password */
666 shared
= lib
->credmgr
->get_shared(lib
->credmgr
, SHARED_EAP
, me
, other
);
669 password
= ascii_to_unicode(shared
->get_key(shared
));
670 shared
->destroy(shared
);
672 if (NtPasswordHash(password
, nt_hash
) == SUCCESS
)
674 chunk_clear(&password
);
677 chunk_clear(&password
);
683 * Process MS-CHAPv2 Challenge Requests
685 static status_t
process_peer_challenge(private_eap_mschapv2_t
*this,
686 eap_payload_t
*in
, eap_payload_t
**out
)
689 eap_mschapv2_header_t
*eap
;
690 eap_mschapv2_challenge_t
*cha
;
691 eap_mschapv2_response_t
*res
;
692 chunk_t data
, peer_challenge
, userid
, username
, nt_hash
;
693 u_int16_t len
= RESPONSE_PAYLOAD_LEN
;
695 data
= in
->get_data(in
);
696 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
698 /* the name MUST be at least one octet long */
699 if (data
.len
< CHALLENGE_PAYLOAD_LEN
+ 1)
701 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
705 cha
= (eap_mschapv2_challenge_t
*)eap
->data
;
707 if (cha
->value_size
!= CHALLENGE_LEN
)
709 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
710 "invalid challenge size");
714 this->mschapv2id
= eap
->ms_chapv2_id
;
715 this->challenge
= chunk_clone(chunk_create(cha
->challenge
, CHALLENGE_LEN
));
717 peer_challenge
= chunk_alloca(CHALLENGE_LEN
);
718 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
719 if (!rng
|| !rng
->get_bytes(rng
, CHALLENGE_LEN
, peer_challenge
.ptr
))
721 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, allocating challenge failed");
727 if (!get_nt_hash(this, this->peer
, this->server
, &nt_hash
))
729 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
730 this->server
, this->peer
);
734 /* we transmit the whole user identity (including the domain part) but
735 * only use the user part when calculating the challenge hash */
736 userid
= this->peer
->get_encoding(this->peer
);
738 username
= extract_username(userid
);
740 if (GenerateStuff(this, this->challenge
, peer_challenge
,
741 username
, nt_hash
) != SUCCESS
)
743 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 generating NT-Response failed");
744 chunk_clear(&nt_hash
);
747 chunk_clear(&nt_hash
);
750 eap
->code
= EAP_RESPONSE
;
751 eap
->identifier
= this->identifier
;
752 eap
->length
= htons(len
);
753 eap
->type
= EAP_MSCHAPV2
;
754 eap
->opcode
= MSCHAPV2_RESPONSE
;
755 eap
->ms_chapv2_id
= this->mschapv2id
;
756 set_ms_length(eap
, len
);
758 res
= (eap_mschapv2_response_t
*)eap
->data
;
759 res
->value_size
= RESPONSE_LEN
;
760 memset(&res
->response
, 0, RESPONSE_LEN
);
761 memcpy(res
->response
.peer_challenge
, peer_challenge
.ptr
, peer_challenge
.len
);
762 memcpy(res
->response
.nt_response
, this->nt_response
.ptr
, this->nt_response
.len
);
763 memcpy(res
->name
, userid
.ptr
, userid
.len
);
765 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
766 this->state
= S_EXPECT_SUCCESS
;
771 * Process MS-CHAPv2 Success Requests
773 static status_t
process_peer_success(private_eap_mschapv2_t
*this,
774 eap_payload_t
*in
, eap_payload_t
**out
)
776 status_t status
= FAILED
;
777 enumerator_t
*enumerator
;
778 eap_mschapv2_header_t
*eap
;
779 chunk_t data
, auth_string
= chunk_empty
;
780 char *message
, *token
, *msg
= NULL
;
782 u_int16_t len
= SHORT_HEADER_LEN
;
784 data
= in
->get_data(in
);
785 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
787 if (data
.len
< AUTH_RESPONSE_LEN
)
789 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
793 message_len
= data
.len
- HEADER_LEN
;
794 message
= malloc(message_len
+ 1);
795 memcpy(message
, eap
->data
, message_len
);
796 message
[message_len
] = '\0';
798 /* S=<auth_string> M=<msg> */
799 enumerator
= enumerator_create_token(message
, " ", " ");
800 while (enumerator
->enumerate(enumerator
, &token
))
802 if (strpfx(token
, "S="))
806 if (strlen(token
) != AUTH_RESPONSE_LEN
- 2)
808 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
809 "invalid auth string");
812 chunk_free(&auth_string
);
813 hex
= chunk_create(token
, AUTH_RESPONSE_LEN
- 2);
814 auth_string
= chunk_from_hex(hex
, NULL
);
816 else if (strpfx(token
, "M="))
823 enumerator
->destroy(enumerator
);
825 if (auth_string
.ptr
== NULL
)
827 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
828 "auth string missing");
832 if (!chunk_equals_const(this->auth_response
, auth_string
))
834 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
838 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg
));
841 eap
->code
= EAP_RESPONSE
;
842 eap
->identifier
= this->identifier
;
843 eap
->length
= htons(len
);
844 eap
->type
= EAP_MSCHAPV2
;
845 eap
->opcode
= MSCHAPV2_SUCCESS
;
847 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
849 this->state
= S_DONE
;
852 chunk_free(&auth_string
);
858 static status_t
process_peer_failure(private_eap_mschapv2_t
*this,
859 eap_payload_t
*in
, eap_payload_t
**out
)
861 status_t status
= FAILED
;
862 enumerator_t
*enumerator
;
863 eap_mschapv2_header_t
*eap
;
865 char *message
, *token
, *msg
= NULL
;
866 int message_len
, error
= 0;
867 chunk_t challenge
= chunk_empty
;
869 data
= in
->get_data(in
);
870 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
872 if (data
.len
< 3) /* we want at least an error code: E=e */
874 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
878 message_len
= data
.len
- HEADER_LEN
;
879 message
= malloc(message_len
+ 1);
880 memcpy(message
, eap
->data
, message_len
);
881 message
[message_len
] = '\0';
883 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
884 enumerator
= enumerator_create_token(message
, " ", " ");
885 while (enumerator
->enumerate(enumerator
, &token
))
887 if (strpfx(token
, "E="))
892 else if (strpfx(token
, "R="))
894 /* ignore retriable */
896 else if (strpfx(token
, "C="))
900 if (strlen(token
) != 2 * CHALLENGE_LEN
)
902 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message:"
903 "invalid challenge");
906 chunk_free(&challenge
);
907 hex
= chunk_create(token
, 2 * CHALLENGE_LEN
);
908 challenge
= chunk_from_hex(hex
, NULL
);
910 else if (strpfx(token
, "V="))
914 else if (strpfx(token
, "M="))
921 enumerator
->destroy(enumerator
);
923 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed with error %N: '%s'",
924 mschapv2_error_names
, error
, sanitize(msg
));
927 * at this point, if the error is retriable, we MAY retry the authentication
928 * or MAY send a Change Password packet.
930 * if the error is not retriable (or if we do neither of the above), we
931 * SHOULD send a Failure Response packet.
932 * windows clients don't do that, and since windows server 2008 r2 behaves
933 * pretty odd if we do send a Failure Response, we just don't send one
934 * either. windows 7 actually sends a delete notify (which, according to the
935 * logs, results in an error on windows server 2008 r2).
937 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
938 * a disabled account but returns the windows error code in a notify payload
943 this->state
= S_DONE
;
946 chunk_free(&challenge
);
952 METHOD(eap_method_t
, process_peer
, status_t
,
953 private_eap_mschapv2_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
956 eap_mschapv2_header_t
*eap
;
958 this->identifier
= in
->get_identifier(in
);
959 data
= in
->get_data(in
);
960 if (data
.len
< SHORT_HEADER_LEN
)
962 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message");
966 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
970 case S_EXPECT_CHALLENGE
:
971 if (eap
->opcode
== MSCHAPV2_CHALLENGE
)
973 return process_peer_challenge(this, in
, out
);
976 case S_EXPECT_SUCCESS
:
979 case MSCHAPV2_SUCCESS
:
980 return process_peer_success(this, in
, out
);
981 case MSCHAPV2_FAILURE
:
982 return process_peer_failure(this, in
, out
);
990 case MSCHAPV2_CHALLENGE
:
991 case MSCHAPV2_SUCCESS
:
992 case MSCHAPV2_FAILURE
:
993 DBG1(DBG_IKE
, "received unexpected EAP-MS-CHAPv2 message with "
994 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
997 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
998 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
1005 * Handles retries on the server
1007 static status_t
process_server_retry(private_eap_mschapv2_t
*this,
1008 eap_payload_t
**out
)
1010 eap_mschapv2_header_t
*eap
;
1013 char msg
[FAILURE_MESSAGE_LEN
];
1014 u_int16_t len
= HEADER_LEN
+ FAILURE_MESSAGE_LEN
- 1; /* no null byte */
1016 if (++this->retries
> MAX_RETRIES
)
1018 /* we MAY send a Failure Request with R=0, but windows 7 does not
1019 * really like that and does not respond with a Failure Response.
1020 * so, to clean up our state we just fail with an EAP-Failure.
1021 * this gives an unknown error on the windows side, but is also fine
1022 * with the standard. */
1023 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed: "
1024 "maximum number of retries reached");
1028 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries
);
1030 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
1031 if (!rng
|| !rng
->get_bytes(rng
, CHALLENGE_LEN
, this->challenge
.ptr
))
1033 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 failed, allocating challenge failed");
1039 chunk_free(&this->nt_response
);
1040 chunk_free(&this->auth_response
);
1041 chunk_free(&this->msk
);
1044 eap
->code
= EAP_REQUEST
;
1045 eap
->identifier
= ++this->identifier
;
1046 eap
->length
= htons(len
);
1047 eap
->type
= EAP_MSCHAPV2
;
1048 eap
->opcode
= MSCHAPV2_FAILURE
;
1049 eap
->ms_chapv2_id
= this->mschapv2id
++; /* increase for each retry */
1050 set_ms_length(eap
, len
);
1052 hex
= chunk_to_hex(this->challenge
, NULL
, TRUE
);
1053 snprintf(msg
, FAILURE_MESSAGE_LEN
, "%s%s", FAILURE_MESSAGE
, hex
.ptr
);
1055 memcpy(eap
->data
, msg
, FAILURE_MESSAGE_LEN
- 1); /* no null byte */
1056 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1058 /* delay the response for some time to make brute-force attacks harder */
1061 /* since the error is retryable the state does not change, we still
1062 * expect an MSCHAPV2_RESPONSE from the peer */
1067 * Process MS-CHAPv2 Response response packets
1069 static status_t
process_server_response(private_eap_mschapv2_t
*this,
1070 eap_payload_t
*in
, eap_payload_t
**out
)
1072 eap_mschapv2_header_t
*eap
;
1073 eap_mschapv2_response_t
*res
;
1074 chunk_t data
, peer_challenge
, username
, nt_hash
;
1075 identification_t
*userid
;
1079 data
= in
->get_data(in
);
1080 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1082 if (data
.len
< RESPONSE_PAYLOAD_LEN
)
1084 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1088 res
= (eap_mschapv2_response_t
*)eap
->data
;
1089 peer_challenge
= chunk_create(res
->response
.peer_challenge
, CHALLENGE_LEN
);
1091 name_len
= min(data
.len
- RESPONSE_PAYLOAD_LEN
, 255);
1092 snprintf(buf
, sizeof(buf
), "%.*s", name_len
, res
->name
);
1093 userid
= identification_create_from_string(buf
);
1094 if (!userid
->equals(userid
, this->peer
))
1096 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 username: '%Y'", userid
);
1098 /* userid can only be destroyed after the last use of username */
1099 username
= extract_username(userid
->get_encoding(userid
));
1101 if (!get_nt_hash(this, this->server
, userid
, &nt_hash
))
1103 DBG1(DBG_IKE
, "no EAP key found for hosts '%Y' - '%Y'",
1104 this->server
, userid
);
1105 /* FIXME: windows 7 always sends the username that is first entered in
1106 * the username box, even, if the user changes it during retries (probably
1107 * to keep consistent with the EAP-Identity).
1108 * thus, we could actually fail here, because retries do not make much
1109 * sense. on the other hand, an attacker could guess usernames, if the
1110 * error messages were different. */
1111 userid
->destroy(userid
);
1112 return process_server_retry(this, out
);
1115 if (GenerateStuff(this, this->challenge
, peer_challenge
,
1116 username
, nt_hash
) != SUCCESS
)
1118 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 verification failed");
1119 userid
->destroy(userid
);
1120 chunk_clear(&nt_hash
);
1123 chunk_clear(&nt_hash
);
1125 if (memeq_const(res
->response
.nt_response
, this->nt_response
.ptr
,
1126 this->nt_response
.len
))
1129 char msg
[AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
)];
1130 u_int16_t len
= HEADER_LEN
+ AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
);
1133 eap
->code
= EAP_REQUEST
;
1134 eap
->identifier
= ++this->identifier
;
1135 eap
->length
= htons(len
);
1136 eap
->type
= EAP_MSCHAPV2
;
1137 eap
->opcode
= MSCHAPV2_SUCCESS
;
1138 eap
->ms_chapv2_id
= this->mschapv2id
;
1139 set_ms_length(eap
, len
);
1141 hex
= chunk_to_hex(this->auth_response
, NULL
, TRUE
);
1142 snprintf(msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
),
1143 "S=%s%s", hex
.ptr
, SUCCESS_MESSAGE
);
1145 memcpy(eap
->data
, msg
, AUTH_RESPONSE_LEN
+ sizeof(SUCCESS_MESSAGE
));
1146 *out
= eap_payload_create_data(chunk_create((void*) eap
, len
));
1148 this->auth
->add(this->auth
, AUTH_RULE_EAP_IDENTITY
, userid
);
1149 this->state
= S_EXPECT_SUCCESS
;
1152 userid
->destroy(userid
);
1153 return process_server_retry(this, out
);
1156 METHOD(eap_method_t
, process_server
, status_t
,
1157 private_eap_mschapv2_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
1159 eap_mschapv2_header_t
*eap
;
1162 if (this->identifier
!= in
->get_identifier(in
))
1164 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: "
1165 "unexpected identifier");
1169 data
= in
->get_data(in
);
1170 if (data
.len
< SHORT_HEADER_LEN
)
1172 DBG1(DBG_IKE
, "received invalid EAP-MS-CHAPv2 message: too short");
1176 eap
= (eap_mschapv2_header_t
*)data
.ptr
;
1178 switch (this->state
)
1180 case S_EXPECT_RESPONSE
:
1181 if (eap
->opcode
== MSCHAPV2_RESPONSE
)
1183 return process_server_response(this, in
, out
);
1186 case S_EXPECT_SUCCESS
:
1187 if (eap
->opcode
== MSCHAPV2_SUCCESS
&&
1196 switch (eap
->opcode
)
1198 case MSCHAPV2_FAILURE
:
1199 /* the client may abort the authentication by sending us a failure
1202 case MSCHAPV2_RESPONSE
:
1203 case MSCHAPV2_SUCCESS
:
1204 DBG1(DBG_IKE
, "received unexpected EAP-MS-CHAPv2 message with "
1205 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
1208 DBG1(DBG_IKE
, "EAP-MS-CHAPv2 received packet with unsupported "
1209 "OpCode (%N)!", mschapv2_opcode_names
, eap
->opcode
);
1215 METHOD(eap_method_t
, get_type
, eap_type_t
,
1216 private_eap_mschapv2_t
*this, u_int32_t
*vendor
)
1219 return EAP_MSCHAPV2
;
1222 METHOD(eap_method_t
, get_msk
, status_t
,
1223 private_eap_mschapv2_t
*this, chunk_t
*msk
)
1233 METHOD(eap_method_t
, get_identifier
, u_int8_t
,
1234 private_eap_mschapv2_t
*this)
1236 return this->identifier
;
1239 METHOD(eap_method_t
, set_identifier
, void,
1240 private_eap_mschapv2_t
*this, u_int8_t identifier
)
1242 this->identifier
= identifier
;
1245 METHOD(eap_method_t
, is_mutual
, bool,
1246 private_eap_mschapv2_t
*this)
1251 METHOD(eap_method_t
, get_auth
, auth_cfg_t
*,
1252 private_eap_mschapv2_t
*this)
1257 METHOD(eap_method_t
, destroy
, void,
1258 private_eap_mschapv2_t
*this)
1260 this->peer
->destroy(this->peer
);
1261 this->server
->destroy(this->server
);
1262 this->auth
->destroy(this->auth
);
1263 chunk_free(&this->challenge
);
1264 chunk_free(&this->nt_response
);
1265 chunk_free(&this->auth_response
);
1266 chunk_free(&this->msk
);
1271 * Generic constructor
1273 static private_eap_mschapv2_t
*eap_mschapv2_create_generic(identification_t
*server
, identification_t
*peer
)
1275 private_eap_mschapv2_t
*this;
1279 .eap_method_interface
= {
1280 .get_type
= _get_type
,
1281 .is_mutual
= _is_mutual
,
1282 .get_msk
= _get_msk
,
1283 .get_identifier
= _get_identifier
,
1284 .set_identifier
= _set_identifier
,
1285 .get_auth
= _get_auth
,
1286 .destroy
= _destroy
,
1289 .peer
= peer
->clone(peer
),
1290 .server
= server
->clone(server
),
1291 .auth
= auth_cfg_create(),
1292 .state
= S_EXPECT_CHALLENGE
,
1301 eap_mschapv2_t
*eap_mschapv2_create_server(identification_t
*server
, identification_t
*peer
)
1303 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
1305 this->public.eap_method_interface
.initiate
= _initiate_server
;
1306 this->public.eap_method_interface
.process
= _process_server
;
1308 /* generate a non-zero identifier */
1311 this->identifier
= random();
1312 } while (!this->identifier
);
1314 this->mschapv2id
= this->identifier
;
1316 return &this->public;
1322 eap_mschapv2_t
*eap_mschapv2_create_peer(identification_t
*server
, identification_t
*peer
)
1324 private_eap_mschapv2_t
*this = eap_mschapv2_create_generic(server
, peer
);
1326 this->public.eap_method_interface
.initiate
= _initiate_peer
;
1327 this->public.eap_method_interface
.process
= _process_peer
;
1329 return &this->public;