eap-mschapv2: Convert UTF-8-encoded passwords
[strongswan.git] / src / libcharon / plugins / eap_mschapv2 / eap_mschapv2.c
1 /*
2 * Copyright (C) 2009-2015 Tobias Brunner
3 * Copyright (C) 2010 Martin Willi
4 * HSR Hochschule fuer Technik Rapperswil
5 *
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>.
10 *
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
14 * for more details.
15 */
16
17 #include "eap_mschapv2.h"
18
19 #include <ctype.h>
20 #include <unistd.h>
21
22 #include <daemon.h>
23 #include <library.h>
24 #include <collections/enumerator.h>
25 #include <crypto/crypters/crypter.h>
26 #include <crypto/hashers/hasher.h>
27
28 typedef struct private_eap_mschapv2_t private_eap_mschapv2_t;
29
30 /**
31 * Private data of an eap_mschapv2_t object.
32 */
33 struct private_eap_mschapv2_t
34 {
35 /**
36 * Public authenticator_t interface.
37 */
38 eap_mschapv2_t public;
39
40 /**
41 * ID of the server
42 */
43 identification_t *server;
44
45 /**
46 * ID of the peer
47 */
48 identification_t *peer;
49
50 /**
51 * challenge sent by the server
52 */
53 chunk_t challenge;
54
55 /**
56 * generated NT-Response
57 */
58 chunk_t nt_response;
59
60 /**
61 * generated Authenticator Response
62 */
63 chunk_t auth_response;
64
65 /**
66 * generated MSK
67 */
68 chunk_t msk;
69
70 /**
71 * EAP message identifier
72 */
73 uint8_t identifier;
74
75 /**
76 * MS-CHAPv2-ID (session ID, increases with each retry)
77 */
78 uint8_t mschapv2id;
79
80 /**
81 * Number of retries
82 */
83 int retries;
84
85 /**
86 * Provide EAP-Identity
87 */
88 auth_cfg_t *auth;
89
90 /**
91 * Current state
92 */
93 enum {
94 S_EXPECT_CHALLENGE,
95 S_EXPECT_RESPONSE,
96 S_EXPECT_SUCCESS,
97 S_DONE,
98 } state;
99 };
100
101 /**
102 * OpCodes
103 */
104 enum mschapv2_opcode_t
105 {
106 MSCHAPV2_CHALLENGE = 1,
107 MSCHAPV2_RESPONSE = 2,
108 MSCHAPV2_SUCCESS = 3,
109 MSCHAPV2_FAILURE = 4,
110 MSCHAPV2_CHANGE_PASSWORD = 7,
111 };
112
113 /**
114 * Names for OpCodes
115 */
116 ENUM_BEGIN(mschapv2_opcode_names, MSCHAPV2_CHALLENGE, MSCHAPV2_FAILURE,
117 "CHALLENGE",
118 "RESPONSE",
119 "SUCCESS",
120 "FAILURE");
121 ENUM_NEXT(mschapv2_opcode_names, MSCHAPV2_CHANGE_PASSWORD, MSCHAPV2_CHANGE_PASSWORD, MSCHAPV2_FAILURE,
122 "CHANGE_PASSWORD");
123 ENUM_END(mschapv2_opcode_names, MSCHAPV2_CHANGE_PASSWORD);
124
125 /**
126 * Error codes
127 */
128 enum mschapv2_error_t
129 {
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,
136 };
137
138 /**
139 * Names for error codes
140 */
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);
151
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)
166
167 /* Number of seconds to delay retries */
168 #define RETRY_DELAY 2
169 /* Maximum number of retries */
170 #define MAX_RETRIES 2
171
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;
175
176 /**
177 * packed EAP-MS-CHAPv2 header struct
178 */
179 struct eap_mschapv2_header_t
180 {
181 /** EAP code (REQUEST/RESPONSE) */
182 uint8_t code;
183 /** unique message identifier */
184 uint8_t identifier;
185 /** length of whole message */
186 uint16_t length;
187 /** EAP type */
188 uint8_t type;
189 /** MS-CHAPv2 OpCode */
190 uint8_t opcode;
191 /** MS-CHAPv2-ID (equals identifier) */
192 uint8_t ms_chapv2_id;
193 /** MS-Length (defined as length - 5) */
194 uint16_t ms_length;
195 /** packet data (determined by OpCode) */
196 uint8_t data[];
197 }__attribute__((__packed__));
198
199 /**
200 * packed data for a MS-CHAPv2 Challenge packet
201 */
202 struct eap_mschapv2_challenge_t
203 {
204 /** Value-Size */
205 uint8_t value_size;
206 /** Challenge */
207 uint8_t challenge[CHALLENGE_LEN];
208 /** Name */
209 uint8_t name[];
210 }__attribute__((__packed__));
211
212 /**
213 * packed data for a MS-CHAPv2 Response packet
214 */
215 struct eap_mschapv2_response_t
216 {
217 /** Value-Size */
218 uint8_t value_size;
219 /** Response */
220 struct
221 {
222 /* Peer-Challenge*/
223 uint8_t peer_challenge[CHALLENGE_LEN];
224 /* Reserved (=zero) */
225 uint8_t peer_reserved[8];
226 /* NT-Response */
227 uint8_t nt_response[24];
228 /* Flags (=zero) */
229 uint8_t flags;
230 } response;
231 /** Name */
232 uint8_t name[];
233 }__attribute__((__packed__));
234
235 /**
236 * Length of the MS-CHAPv2 header
237 */
238 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
239
240 /**
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)
243 */
244 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
245
246 /**
247 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
248 * at least one octet)
249 */
250 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
251
252 /**
253 * The minimum length of an MS-CHAPv2 Response packet
254 */
255 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
256
257
258 /**
259 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
260 */
261 static chunk_t ExpandDESKey(chunk_t key)
262 {
263 static const u_char bitmask[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
264 int i;
265 u_char carry = 0;
266 chunk_t expanded;
267
268 /* expand the 7 octets to 8 octets */
269 expanded = chunk_alloc(8);
270 for (i = 0; i < 7; i++)
271 {
272 expanded.ptr[i] = ((key.ptr[i] & bitmask[i]) >> i) | (carry << (8 - i));
273 carry = key.ptr[i] & ~bitmask[i];
274 }
275 expanded.ptr[7] = carry << 1;
276
277 /* add parity bits to each octet */
278 for (i = 0; i < 8; i++)
279 {
280 u_char val = expanded.ptr[i];
281 val = (val ^ (val >> 4)) & 0x0f;
282 expanded.ptr[i] |= (0x9669 >> val) & 1;
283 }
284 return expanded;
285 }
286
287 /**
288 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
289 */
290 static status_t NtPasswordHash(chunk_t password, chunk_t *password_hash)
291 {
292 hasher_t *hasher;
293 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD4);
294 if (hasher == NULL)
295 {
296 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
297 return FAILED;
298 }
299 if (!hasher->allocate_hash(hasher, password, password_hash))
300 {
301 hasher->destroy(hasher);
302 return FAILED;
303 }
304 hasher->destroy(hasher);
305 return SUCCESS;
306 }
307
308 /**
309 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
310 * username (without domain part)] with SHA1)
311 */
312 static status_t ChallengeHash(chunk_t peer_challenge, chunk_t server_challenge,
313 chunk_t username, chunk_t *challenge_hash)
314 {
315 chunk_t concat;
316 hasher_t *hasher;
317 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
318 if (hasher == NULL)
319 {
320 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
321 return FAILED;
322 }
323 concat = chunk_cata("ccc", peer_challenge, server_challenge, username);
324 if (!hasher->allocate_hash(hasher, concat, challenge_hash))
325 {
326 hasher->destroy(hasher);
327 return FAILED;
328 }
329 hasher->destroy(hasher);
330 /* we need only the first 8 octets */
331 challenge_hash->len = 8;
332 return SUCCESS;
333 }
334
335 /**
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
338 * the results).
339 */
340 static status_t ChallengeResponse(chunk_t challenge_hash, chunk_t password_hash,
341 chunk_t *response)
342 {
343 int i;
344 crypter_t *crypter;
345 chunk_t keys[3], z_password_hash;
346 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_DES_ECB, 8);
347 if (crypter == NULL)
348 {
349 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
350 return FAILED;
351 }
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]);
358
359 *response = chunk_alloc(24);
360 for (i = 0; i < 3; i++)
361 {
362 chunk_t expanded, encrypted;
363
364 expanded = ExpandDESKey(keys[i]);
365 if (!crypter->set_key(crypter, expanded) ||
366 !crypter->encrypt(crypter, challenge_hash, chunk_empty, &encrypted))
367 {
368 chunk_clear(&expanded);
369 crypter->destroy(crypter);
370 return FAILED;
371 }
372 memcpy(&response->ptr[i * 8], encrypted.ptr, encrypted.len);
373 chunk_clear(&encrypted);
374 chunk_clear(&expanded);
375 }
376 crypter->destroy(crypter);
377 return SUCCESS;
378 }
379
380 /**
381 * Computes the authenticator response
382 */
383 static status_t AuthenticatorResponse(chunk_t password_hash_hash,
384 chunk_t challenge_hash, chunk_t nt_response, chunk_t *response)
385 {
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,
396 0x6E);
397 chunk_t digest = chunk_empty, concat;
398 hasher_t *hasher;
399
400 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
401 if (hasher == NULL)
402 {
403 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
404 return FAILED;
405 }
406
407 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
408 if (!hasher->allocate_hash(hasher, concat, &digest))
409 {
410 hasher->destroy(hasher);
411 return FAILED;
412 }
413 concat = chunk_cata("ccc", digest, challenge_hash, magic2);
414 if (!hasher->allocate_hash(hasher, concat, response))
415 {
416 hasher->destroy(hasher);
417 return FAILED;
418 }
419 hasher->destroy(hasher);
420 chunk_free(&digest);
421 return SUCCESS;
422 }
423
424 /**
425 * Generate the master session key according to RFC3079
426 */
427 static status_t GenerateMSK(chunk_t password_hash_hash,
428 chunk_t nt_response, chunk_t *msk)
429 {
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;
470 hasher_t *hasher;
471
472 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
473 if (hasher == NULL)
474 {
475 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
476 return FAILED;
477 }
478
479 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
480 if (!hasher->get_hash(hasher, concat, master_key))
481 {
482 hasher->destroy(hasher);
483 return FAILED;
484 }
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))
488 {
489 hasher->destroy(hasher);
490 return FAILED;
491 }
492 concat = chunk_cata("cccc", master, shapad1, magic3, shapad2);
493 if (!hasher->get_hash(hasher, concat, master_send_key))
494 {
495 hasher->destroy(hasher);
496 return FAILED;
497 }
498
499 *msk = chunk_cat("cccc", chunk_create(master_receive_key, 16),
500 chunk_create(master_send_key, 16), keypad, keypad);
501
502 hasher->destroy(hasher);
503 return SUCCESS;
504 }
505
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)
509 {
510 status_t status = FAILED;
511 chunk_t nt_hash_hash = chunk_empty, challenge_hash = chunk_empty;
512
513 if (NtPasswordHash(nt_hash, &nt_hash_hash) != SUCCESS)
514 {
515 goto error;
516 }
517 if (ChallengeHash(peer_challenge, server_challenge, username,
518 &challenge_hash) != SUCCESS)
519 {
520 goto error;
521 }
522 if (ChallengeResponse(challenge_hash, nt_hash,
523 &this->nt_response) != SUCCESS)
524 {
525 goto error;
526 }
527 if (AuthenticatorResponse(nt_hash_hash, challenge_hash,
528 this->nt_response, &this->auth_response) != SUCCESS)
529 {
530 goto error;
531 }
532 if (GenerateMSK(nt_hash_hash, this->nt_response, &this->msk) != SUCCESS)
533 {
534 goto error;
535 }
536
537 status = SUCCESS;
538
539 error:
540 chunk_free(&nt_hash_hash);
541 chunk_free(&challenge_hash);
542 return status;
543 }
544
545 /**
546 * Try to decode the UTF-8 byte sequence utf8 points to and advance the
547 * position. Returns FALSE if the byte sequence is incomplete (the actual
548 * values are not validated).
549 */
550 static inline bool decode_utf8(chunk_t *utf8, uint32_t *c)
551 {
552 uint8_t b1, b2, b3, b4;
553 int i = 0;
554
555 if (!utf8->len)
556 {
557 return FALSE;
558 }
559
560 b1 = utf8->ptr[i++];
561 if (b1 & 0x80)
562 {
563 if (i == utf8->len)
564 {
565 return FALSE;
566 }
567 b2 = utf8->ptr[i++];
568 if (b1 < 0xe0)
569 { /* two-byte encoding */
570 *c = ((b1 & 0x1f) << 6) | (b2 & 0x3f);
571 goto done;
572 }
573 if (i == utf8->len)
574 {
575 return FALSE;
576 }
577 b3 = utf8->ptr[i++];
578 if (b1 < 0xf0)
579 { /* three-byte encoding */
580 *c = ((b1 & 0x0f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f);
581 goto done;
582 }
583 if (i == utf8->len)
584 {
585 return FALSE;
586 }
587 b4 = utf8->ptr[i++];
588 /* four-byte encoding */
589 *c = ((b1 & 0x07) << 18) | ((b2 & 0x3f) << 12) |
590 ((b3 & 0x3f) << 6) | (b4 & 0x3f);
591 }
592 else
593 { /* single-byte ASCII */
594 *c = b1;
595 }
596
597 done:
598 *utf8 = chunk_skip(*utf8, i);
599 return TRUE;
600 }
601
602 /**
603 * Encode the given code point (or surrogate) as UTF-16LE where utf16 points
604 * to and advance the position.
605 */
606 static inline void encode_utf16le_single(chunk_t *utf16, uint16_t c)
607 {
608 if (utf16->len >= 2)
609 {
610 utf16->ptr[0] = c & 0xff;
611 utf16->ptr[1] = c >> 8;
612 *utf16 = chunk_skip(*utf16, 2);
613 }
614 }
615
616 /**
617 * Encode the given code point as UTF-16LE where utf16 points to and advance
618 * the position. Invalid code points are ingored.
619 */
620 static inline void encode_utf16le(chunk_t *utf16, uint32_t c)
621 {
622
623 if (c >= 0xd800 && (c <= 0xdfff || c > 0x10ffff ||
624 (0xfdd0 <= c && (c <= 0xfdef || (c & 0xfffe) == 0xfffe))))
625 { /* ignore code points that are not characters: single surrogates
626 * 0xd800..0xdfff, any value > 0x10ffff, 0xfdd0..0xfdef as per
627 * Unicode 3.1, the last two in each plane 0x__fffe and 0x__ffff. */
628 return;
629 }
630 if (c < 0x10000)
631 { /* BMP code points */
632 encode_utf16le_single(utf16, c);
633 }
634 else
635 { /* supplementary code points are split into two 16-bit surrogates */
636 encode_utf16le_single(utf16, 0xd7c0 + (c >> 10));
637 encode_utf16le_single(utf16, 0xdc00 | (c & 0x3ff));
638 }
639 }
640
641 /**
642 * Converts an (assumed) UTF-8 string into a UTF-16 (little-endian) string
643 */
644 static chunk_t utf8_to_utf16le(chunk_t utf8)
645 {
646 chunk_t utf16, buf;
647 uint32_t c;
648
649 /* unless the input is ASCII-only this will generally be too much, but
650 * doesn't really matter for the length of strings processed here */
651 utf16 = buf = chunk_alloc(utf8.len * 2);
652
653 while (decode_utf8(&utf8, &c))
654 {
655 encode_utf16le(&buf, c);
656 }
657
658 utf16.len = utf16.len - buf.len;
659
660 return utf16;
661 }
662
663 /**
664 * sanitize a string for printing
665 */
666 static char* sanitize(char *str)
667 {
668 char *pos = str;
669
670 while (pos && *pos)
671 {
672 if (!isprint(*pos))
673 {
674 *pos = '?';
675 }
676 pos++;
677 }
678 return str;
679 }
680
681 /**
682 * Returns a chunk of just the username part of the given user identity.
683 * Note: the chunk points to internal data of the given chunk
684 */
685 static chunk_t extract_username(chunk_t id)
686 {
687 char *has_domain;
688
689 has_domain = (char*)memchr(id.ptr, '\\', id.len);
690 if (has_domain)
691 {
692 int len;
693 has_domain++; /* skip the backslash */
694 len = id.len - ((u_char*)has_domain - id.ptr);
695 return len > 0 ? chunk_create(has_domain, len) : chunk_empty;
696 }
697 return id;
698 }
699
700 /**
701 * Set the ms_length field using aligned write
702 */
703 static void set_ms_length(eap_mschapv2_header_t *eap, uint16_t len)
704 {
705 len = htons(len - 5);
706 memcpy(&eap->ms_length, &len, sizeof(uint16_t));
707 }
708
709 METHOD(eap_method_t, initiate_peer, status_t,
710 private_eap_mschapv2_t *this, eap_payload_t **out)
711 {
712 /* peer never initiates */
713 return FAILED;
714 }
715
716 METHOD(eap_method_t, initiate_server, status_t,
717 private_eap_mschapv2_t *this, eap_payload_t **out)
718 {
719 rng_t *rng;
720 eap_mschapv2_header_t *eap;
721 eap_mschapv2_challenge_t *cha;
722 const char *name = MSCHAPV2_HOST_NAME;
723 uint16_t len = CHALLENGE_PAYLOAD_LEN + sizeof(MSCHAPV2_HOST_NAME) - 1;
724
725 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
726 if (!rng || !rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge))
727 {
728 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no challenge");
729 DESTROY_IF(rng);
730 return FAILED;
731 }
732 rng->destroy(rng);
733
734 eap = alloca(len);
735 eap->code = EAP_REQUEST;
736 eap->identifier = this->identifier;
737 eap->length = htons(len);
738 eap->type = EAP_MSCHAPV2;
739 eap->opcode = MSCHAPV2_CHALLENGE;
740 eap->ms_chapv2_id = this->mschapv2id;
741 set_ms_length(eap, len);
742
743 cha = (eap_mschapv2_challenge_t*)eap->data;
744 cha->value_size = CHALLENGE_LEN;
745 memcpy(cha->challenge, this->challenge.ptr, this->challenge.len);
746 memcpy(cha->name, name, sizeof(MSCHAPV2_HOST_NAME) - 1);
747
748 *out = eap_payload_create_data(chunk_create((void*) eap, len));
749 this->state = S_EXPECT_RESPONSE;
750 return NEED_MORE;
751 }
752
753 static bool get_nt_hash(private_eap_mschapv2_t *this, identification_t *me,
754 identification_t *other, chunk_t *nt_hash)
755 {
756 shared_key_t *shared;
757 chunk_t password;
758
759 /* try to find a stored NT_HASH first */
760 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_NT_HASH, me, other);
761 if (shared )
762 {
763 *nt_hash = chunk_clone(shared->get_key(shared));
764 shared->destroy(shared);
765 return TRUE;
766 }
767
768 /* fallback to plaintext password */
769 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, me, other);
770 if (shared)
771 {
772 password = utf8_to_utf16le(shared->get_key(shared));
773 shared->destroy(shared);
774
775 if (NtPasswordHash(password, nt_hash) == SUCCESS)
776 {
777 chunk_clear(&password);
778 return TRUE;
779 }
780 chunk_clear(&password);
781 }
782 return FALSE;
783 }
784
785 /**
786 * Process MS-CHAPv2 Challenge Requests
787 */
788 static status_t process_peer_challenge(private_eap_mschapv2_t *this,
789 eap_payload_t *in, eap_payload_t **out)
790 {
791 rng_t *rng;
792 eap_mschapv2_header_t *eap;
793 eap_mschapv2_challenge_t *cha;
794 eap_mschapv2_response_t *res;
795 chunk_t data, peer_challenge, userid, username, nt_hash;
796 uint16_t len = RESPONSE_PAYLOAD_LEN;
797
798 data = in->get_data(in);
799 eap = (eap_mschapv2_header_t*)data.ptr;
800
801 /* the name MUST be at least one octet long */
802 if (data.len < CHALLENGE_PAYLOAD_LEN + 1)
803 {
804 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
805 return FAILED;
806 }
807
808 cha = (eap_mschapv2_challenge_t*)eap->data;
809
810 if (cha->value_size != CHALLENGE_LEN)
811 {
812 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
813 "invalid challenge size");
814 return FAILED;
815 }
816
817 this->mschapv2id = eap->ms_chapv2_id;
818 this->challenge = chunk_clone(chunk_create(cha->challenge, CHALLENGE_LEN));
819
820 peer_challenge = chunk_alloca(CHALLENGE_LEN);
821 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
822 if (!rng || !rng->get_bytes(rng, CHALLENGE_LEN, peer_challenge.ptr))
823 {
824 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, allocating challenge failed");
825 DESTROY_IF(rng);
826 return FAILED;
827 }
828 rng->destroy(rng);
829
830 if (!get_nt_hash(this, this->peer, this->server, &nt_hash))
831 {
832 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
833 this->server, this->peer);
834 return NOT_FOUND;
835 }
836
837 /* we transmit the whole user identity (including the domain part) but
838 * only use the user part when calculating the challenge hash */
839 userid = this->peer->get_encoding(this->peer);
840 len += userid.len;
841 username = extract_username(userid);
842
843 if (GenerateStuff(this, this->challenge, peer_challenge,
844 username, nt_hash) != SUCCESS)
845 {
846 DBG1(DBG_IKE, "EAP-MS-CHAPv2 generating NT-Response failed");
847 chunk_clear(&nt_hash);
848 return FAILED;
849 }
850 chunk_clear(&nt_hash);
851
852 eap = alloca(len);
853 eap->code = EAP_RESPONSE;
854 eap->identifier = this->identifier;
855 eap->length = htons(len);
856 eap->type = EAP_MSCHAPV2;
857 eap->opcode = MSCHAPV2_RESPONSE;
858 eap->ms_chapv2_id = this->mschapv2id;
859 set_ms_length(eap, len);
860
861 res = (eap_mschapv2_response_t*)eap->data;
862 res->value_size = RESPONSE_LEN;
863 memset(&res->response, 0, RESPONSE_LEN);
864 memcpy(res->response.peer_challenge, peer_challenge.ptr, peer_challenge.len);
865 memcpy(res->response.nt_response, this->nt_response.ptr, this->nt_response.len);
866 memcpy(res->name, userid.ptr, userid.len);
867
868 *out = eap_payload_create_data(chunk_create((void*) eap, len));
869 this->state = S_EXPECT_SUCCESS;
870 return NEED_MORE;
871 }
872
873 /**
874 * Process MS-CHAPv2 Success Requests
875 */
876 static status_t process_peer_success(private_eap_mschapv2_t *this,
877 eap_payload_t *in, eap_payload_t **out)
878 {
879 status_t status = FAILED;
880 enumerator_t *enumerator;
881 eap_mschapv2_header_t *eap;
882 chunk_t data, auth_string = chunk_empty;
883 char *message, *token, *msg = NULL;
884 int message_len;
885 uint16_t len = SHORT_HEADER_LEN;
886
887 data = in->get_data(in);
888 eap = (eap_mschapv2_header_t*)data.ptr;
889
890 if (data.len < AUTH_RESPONSE_LEN)
891 {
892 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
893 return FAILED;
894 }
895
896 message_len = data.len - HEADER_LEN;
897 message = malloc(message_len + 1);
898 memcpy(message, eap->data, message_len);
899 message[message_len] = '\0';
900
901 /* S=<auth_string> M=<msg> */
902 enumerator = enumerator_create_token(message, " ", " ");
903 while (enumerator->enumerate(enumerator, &token))
904 {
905 if (strpfx(token, "S="))
906 {
907 chunk_t hex;
908 token += 2;
909 if (strlen(token) != AUTH_RESPONSE_LEN - 2)
910 {
911 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
912 "invalid auth string");
913 goto error;
914 }
915 chunk_free(&auth_string);
916 hex = chunk_create(token, AUTH_RESPONSE_LEN - 2);
917 auth_string = chunk_from_hex(hex, NULL);
918 }
919 else if (strpfx(token, "M="))
920 {
921 token += 2;
922 free(msg);
923 msg = strdup(token);
924 }
925 }
926 enumerator->destroy(enumerator);
927
928 if (auth_string.ptr == NULL)
929 {
930 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
931 "auth string missing");
932 goto error;
933 }
934
935 if (!chunk_equals_const(this->auth_response, auth_string))
936 {
937 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
938 goto error;
939 }
940
941 DBG1(DBG_IKE, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg));
942
943 eap = alloca(len);
944 eap->code = EAP_RESPONSE;
945 eap->identifier = this->identifier;
946 eap->length = htons(len);
947 eap->type = EAP_MSCHAPV2;
948 eap->opcode = MSCHAPV2_SUCCESS;
949
950 *out = eap_payload_create_data(chunk_create((void*) eap, len));
951 status = NEED_MORE;
952 this->state = S_DONE;
953
954 error:
955 chunk_free(&auth_string);
956 free(message);
957 free(msg);
958 return status;
959 }
960
961 static status_t process_peer_failure(private_eap_mschapv2_t *this,
962 eap_payload_t *in, eap_payload_t **out)
963 {
964 status_t status = FAILED;
965 enumerator_t *enumerator;
966 eap_mschapv2_header_t *eap;
967 chunk_t data;
968 char *message, *token, *msg = NULL;
969 int message_len, error = 0;
970 chunk_t challenge = chunk_empty;
971
972 data = in->get_data(in);
973 eap = (eap_mschapv2_header_t*)data.ptr;
974
975 if (data.len < 3) /* we want at least an error code: E=e */
976 {
977 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
978 return FAILED;
979 }
980
981 message_len = data.len - HEADER_LEN;
982 message = malloc(message_len + 1);
983 memcpy(message, eap->data, message_len);
984 message[message_len] = '\0';
985
986 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
987 enumerator = enumerator_create_token(message, " ", " ");
988 while (enumerator->enumerate(enumerator, &token))
989 {
990 if (strpfx(token, "E="))
991 {
992 token += 2;
993 error = atoi(token);
994 }
995 else if (strpfx(token, "R="))
996 {
997 /* ignore retriable */
998 }
999 else if (strpfx(token, "C="))
1000 {
1001 chunk_t hex;
1002 token += 2;
1003 if (strlen(token) != 2 * CHALLENGE_LEN)
1004 {
1005 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message:"
1006 "invalid challenge");
1007 goto error;
1008 }
1009 chunk_free(&challenge);
1010 hex = chunk_create(token, 2 * CHALLENGE_LEN);
1011 challenge = chunk_from_hex(hex, NULL);
1012 }
1013 else if (strpfx(token, "V="))
1014 {
1015 /* ignore version */
1016 }
1017 else if (strpfx(token, "M="))
1018 {
1019 token += 2;
1020 free(msg);
1021 msg = strdup(token);
1022 }
1023 }
1024 enumerator->destroy(enumerator);
1025
1026 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed with error %N: '%s'",
1027 mschapv2_error_names, error, sanitize(msg));
1028
1029 /**
1030 * at this point, if the error is retriable, we MAY retry the authentication
1031 * or MAY send a Change Password packet.
1032 *
1033 * if the error is not retriable (or if we do neither of the above), we
1034 * SHOULD send a Failure Response packet.
1035 * windows clients don't do that, and since windows server 2008 r2 behaves
1036 * pretty odd if we do send a Failure Response, we just don't send one
1037 * either. windows 7 actually sends a delete notify (which, according to the
1038 * logs, results in an error on windows server 2008 r2).
1039 *
1040 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
1041 * a disabled account but returns the windows error code in a notify payload
1042 * of type 12345.
1043 */
1044
1045 status = FAILED;
1046 this->state = S_DONE;
1047
1048 error:
1049 chunk_free(&challenge);
1050 free(message);
1051 free(msg);
1052 return status;
1053 }
1054
1055 METHOD(eap_method_t, process_peer, status_t,
1056 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
1057 {
1058 chunk_t data;
1059 eap_mschapv2_header_t *eap;
1060
1061 this->identifier = in->get_identifier(in);
1062 data = in->get_data(in);
1063 if (data.len < SHORT_HEADER_LEN)
1064 {
1065 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message");
1066 return FAILED;
1067 }
1068
1069 eap = (eap_mschapv2_header_t*)data.ptr;
1070
1071 switch (this->state)
1072 {
1073 case S_EXPECT_CHALLENGE:
1074 if (eap->opcode == MSCHAPV2_CHALLENGE)
1075 {
1076 return process_peer_challenge(this, in, out);
1077 }
1078 break;
1079 case S_EXPECT_SUCCESS:
1080 switch (eap->opcode)
1081 {
1082 case MSCHAPV2_SUCCESS:
1083 return process_peer_success(this, in, out);
1084 case MSCHAPV2_FAILURE:
1085 return process_peer_failure(this, in, out);
1086 }
1087 break;
1088 default:
1089 break;
1090 }
1091 switch (eap->opcode)
1092 {
1093 case MSCHAPV2_CHALLENGE:
1094 case MSCHAPV2_SUCCESS:
1095 case MSCHAPV2_FAILURE:
1096 DBG1(DBG_IKE, "received unexpected EAP-MS-CHAPv2 message with "
1097 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1098 break;
1099 default:
1100 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
1101 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1102 break;
1103 }
1104 return FAILED;
1105 }
1106
1107 /**
1108 * Handles retries on the server
1109 */
1110 static status_t process_server_retry(private_eap_mschapv2_t *this,
1111 eap_payload_t **out)
1112 {
1113 eap_mschapv2_header_t *eap;
1114 rng_t *rng;
1115 chunk_t hex;
1116 char msg[FAILURE_MESSAGE_LEN];
1117 uint16_t len = HEADER_LEN + FAILURE_MESSAGE_LEN - 1; /* no null byte */
1118
1119 if (++this->retries > MAX_RETRIES)
1120 {
1121 /* we MAY send a Failure Request with R=0, but windows 7 does not
1122 * really like that and does not respond with a Failure Response.
1123 * so, to clean up our state we just fail with an EAP-Failure.
1124 * this gives an unknown error on the windows side, but is also fine
1125 * with the standard. */
1126 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed: "
1127 "maximum number of retries reached");
1128 return FAILED;
1129 }
1130
1131 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries);
1132
1133 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
1134 if (!rng || !rng->get_bytes(rng, CHALLENGE_LEN, this->challenge.ptr))
1135 {
1136 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, allocating challenge failed");
1137 DESTROY_IF(rng);
1138 return FAILED;
1139 }
1140 rng->destroy(rng);
1141
1142 chunk_free(&this->nt_response);
1143 chunk_free(&this->auth_response);
1144 chunk_free(&this->msk);
1145
1146 eap = alloca(len);
1147 eap->code = EAP_REQUEST;
1148 eap->identifier = ++this->identifier;
1149 eap->length = htons(len);
1150 eap->type = EAP_MSCHAPV2;
1151 eap->opcode = MSCHAPV2_FAILURE;
1152 eap->ms_chapv2_id = this->mschapv2id++; /* increase for each retry */
1153 set_ms_length(eap, len);
1154
1155 hex = chunk_to_hex(this->challenge, NULL, TRUE);
1156 snprintf(msg, FAILURE_MESSAGE_LEN, "%s%s", FAILURE_MESSAGE, hex.ptr);
1157 chunk_free(&hex);
1158 memcpy(eap->data, msg, FAILURE_MESSAGE_LEN - 1); /* no null byte */
1159 *out = eap_payload_create_data(chunk_create((void*) eap, len));
1160
1161 /* delay the response for some time to make brute-force attacks harder */
1162 sleep(RETRY_DELAY);
1163
1164 /* since the error is retryable the state does not change, we still
1165 * expect an MSCHAPV2_RESPONSE from the peer */
1166 return NEED_MORE;
1167 }
1168
1169 /**
1170 * Process MS-CHAPv2 Response response packets
1171 */
1172 static status_t process_server_response(private_eap_mschapv2_t *this,
1173 eap_payload_t *in, eap_payload_t **out)
1174 {
1175 eap_mschapv2_header_t *eap;
1176 eap_mschapv2_response_t *res;
1177 chunk_t data, peer_challenge, username, nt_hash;
1178 identification_t *userid;
1179 int name_len;
1180 char buf[256];
1181
1182 data = in->get_data(in);
1183 eap = (eap_mschapv2_header_t*)data.ptr;
1184
1185 if (data.len < RESPONSE_PAYLOAD_LEN)
1186 {
1187 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1188 return FAILED;
1189 }
1190
1191 res = (eap_mschapv2_response_t*)eap->data;
1192 peer_challenge = chunk_create(res->response.peer_challenge, CHALLENGE_LEN);
1193
1194 name_len = min(data.len - RESPONSE_PAYLOAD_LEN, 255);
1195 snprintf(buf, sizeof(buf), "%.*s", name_len, res->name);
1196 userid = identification_create_from_string(buf);
1197 if (!userid->equals(userid, this->peer))
1198 {
1199 DBG1(DBG_IKE, "EAP-MS-CHAPv2 username: '%Y'", userid);
1200 }
1201 /* userid can only be destroyed after the last use of username */
1202 username = extract_username(userid->get_encoding(userid));
1203
1204 if (!get_nt_hash(this, this->server, userid, &nt_hash))
1205 {
1206 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
1207 this->server, userid);
1208 /* FIXME: windows 7 always sends the username that is first entered in
1209 * the username box, even, if the user changes it during retries (probably
1210 * to keep consistent with the EAP-Identity).
1211 * thus, we could actually fail here, because retries do not make much
1212 * sense. on the other hand, an attacker could guess usernames, if the
1213 * error messages were different. */
1214 userid->destroy(userid);
1215 return process_server_retry(this, out);
1216 }
1217
1218 if (GenerateStuff(this, this->challenge, peer_challenge,
1219 username, nt_hash) != SUCCESS)
1220 {
1221 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
1222 userid->destroy(userid);
1223 chunk_clear(&nt_hash);
1224 return FAILED;
1225 }
1226 chunk_clear(&nt_hash);
1227
1228 if (memeq_const(res->response.nt_response, this->nt_response.ptr,
1229 this->nt_response.len))
1230 {
1231 chunk_t hex;
1232 char msg[AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE)];
1233 uint16_t len = HEADER_LEN + AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE);
1234
1235 eap = alloca(len);
1236 eap->code = EAP_REQUEST;
1237 eap->identifier = ++this->identifier;
1238 eap->length = htons(len);
1239 eap->type = EAP_MSCHAPV2;
1240 eap->opcode = MSCHAPV2_SUCCESS;
1241 eap->ms_chapv2_id = this->mschapv2id;
1242 set_ms_length(eap, len);
1243
1244 hex = chunk_to_hex(this->auth_response, NULL, TRUE);
1245 snprintf(msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE),
1246 "S=%s%s", hex.ptr, SUCCESS_MESSAGE);
1247 chunk_free(&hex);
1248 memcpy(eap->data, msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE));
1249 *out = eap_payload_create_data(chunk_create((void*) eap, len));
1250
1251 this->auth->add(this->auth, AUTH_RULE_EAP_IDENTITY, userid);
1252 this->state = S_EXPECT_SUCCESS;
1253 return NEED_MORE;
1254 }
1255 userid->destroy(userid);
1256 return process_server_retry(this, out);
1257 }
1258
1259 METHOD(eap_method_t, process_server, status_t,
1260 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
1261 {
1262 eap_mschapv2_header_t *eap;
1263 chunk_t data;
1264
1265 if (this->identifier != in->get_identifier(in))
1266 {
1267 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
1268 "unexpected identifier");
1269 return FAILED;
1270 }
1271
1272 data = in->get_data(in);
1273 if (data.len < SHORT_HEADER_LEN)
1274 {
1275 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1276 return FAILED;
1277 }
1278
1279 eap = (eap_mschapv2_header_t*)data.ptr;
1280
1281 switch (this->state)
1282 {
1283 case S_EXPECT_RESPONSE:
1284 if (eap->opcode == MSCHAPV2_RESPONSE)
1285 {
1286 return process_server_response(this, in, out);
1287 }
1288 break;
1289 case S_EXPECT_SUCCESS:
1290 if (eap->opcode == MSCHAPV2_SUCCESS &&
1291 this->msk.ptr)
1292 {
1293 return SUCCESS;
1294 }
1295 break;
1296 default:
1297 break;
1298 }
1299 switch (eap->opcode)
1300 {
1301 case MSCHAPV2_FAILURE:
1302 /* the client may abort the authentication by sending us a failure
1303 * in any state */
1304 return FAILED;
1305 case MSCHAPV2_RESPONSE:
1306 case MSCHAPV2_SUCCESS:
1307 DBG1(DBG_IKE, "received unexpected EAP-MS-CHAPv2 message with "
1308 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1309 break;
1310 default:
1311 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
1312 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1313 break;
1314 }
1315 return FAILED;
1316 }
1317
1318 METHOD(eap_method_t, get_type, eap_type_t,
1319 private_eap_mschapv2_t *this, uint32_t *vendor)
1320 {
1321 *vendor = 0;
1322 return EAP_MSCHAPV2;
1323 }
1324
1325 METHOD(eap_method_t, get_msk, status_t,
1326 private_eap_mschapv2_t *this, chunk_t *msk)
1327 {
1328 if (this->msk.ptr)
1329 {
1330 *msk = this->msk;
1331 return SUCCESS;
1332 }
1333 return FAILED;
1334 }
1335
1336 METHOD(eap_method_t, get_identifier, uint8_t,
1337 private_eap_mschapv2_t *this)
1338 {
1339 return this->identifier;
1340 }
1341
1342 METHOD(eap_method_t, set_identifier, void,
1343 private_eap_mschapv2_t *this, uint8_t identifier)
1344 {
1345 this->identifier = identifier;
1346 }
1347
1348 METHOD(eap_method_t, is_mutual, bool,
1349 private_eap_mschapv2_t *this)
1350 {
1351 return FALSE;
1352 }
1353
1354 METHOD(eap_method_t, get_auth, auth_cfg_t*,
1355 private_eap_mschapv2_t *this)
1356 {
1357 return this->auth;
1358 }
1359
1360 METHOD(eap_method_t, destroy, void,
1361 private_eap_mschapv2_t *this)
1362 {
1363 this->peer->destroy(this->peer);
1364 this->server->destroy(this->server);
1365 this->auth->destroy(this->auth);
1366 chunk_free(&this->challenge);
1367 chunk_free(&this->nt_response);
1368 chunk_free(&this->auth_response);
1369 chunk_free(&this->msk);
1370 free(this);
1371 }
1372
1373 /**
1374 * Generic constructor
1375 */
1376 static private_eap_mschapv2_t *eap_mschapv2_create_generic(identification_t *server, identification_t *peer)
1377 {
1378 private_eap_mschapv2_t *this;
1379
1380 INIT(this,
1381 .public = {
1382 .eap_method_interface = {
1383 .get_type = _get_type,
1384 .is_mutual = _is_mutual,
1385 .get_msk = _get_msk,
1386 .get_identifier = _get_identifier,
1387 .set_identifier = _set_identifier,
1388 .get_auth = _get_auth,
1389 .destroy = _destroy,
1390 },
1391 },
1392 .peer = peer->clone(peer),
1393 .server = server->clone(server),
1394 .auth = auth_cfg_create(),
1395 .state = S_EXPECT_CHALLENGE,
1396 );
1397
1398 return this;
1399 }
1400
1401 /*
1402 * see header
1403 */
1404 eap_mschapv2_t *eap_mschapv2_create_server(identification_t *server, identification_t *peer)
1405 {
1406 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1407
1408 this->public.eap_method_interface.initiate = _initiate_server;
1409 this->public.eap_method_interface.process = _process_server;
1410
1411 /* generate a non-zero identifier */
1412 do
1413 {
1414 this->identifier = random();
1415 } while (!this->identifier);
1416
1417 this->mschapv2id = this->identifier;
1418
1419 return &this->public;
1420 }
1421
1422 /*
1423 * see header
1424 */
1425 eap_mschapv2_t *eap_mschapv2_create_peer(identification_t *server, identification_t *peer)
1426 {
1427 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1428
1429 this->public.eap_method_interface.initiate = _initiate_peer;
1430 this->public.eap_method_interface.process = _process_peer;
1431
1432 return &this->public;
1433 }