Moved data structures to new collections subfolder
[strongswan.git] / src / libcharon / plugins / eap_mschapv2 / eap_mschapv2.c
1 /*
2 * Copyright (C) 2009 Tobias Brunner
3 * Copyright (C) 2010 Martin Willi
4 * 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 u_int8_t identifier;
74
75 /**
76 * MS-CHAPv2-ID (session ID, increases with each retry)
77 */
78 u_int8_t mschapv2id;
79
80 /**
81 * Number of retries
82 */
83 int retries;
84 };
85
86 /**
87 * OpCodes
88 */
89 enum mschapv2_opcode_t
90 {
91 MSCHAPV2_CHALLENGE = 1,
92 MSCHAPV2_RESPONSE = 2,
93 MSCHAPV2_SUCCESS = 3,
94 MSCHAPV2_FAILURE = 4,
95 MSCHAPV2_CHANGE_PASSWORD = 7,
96 };
97
98 /**
99 * Names for OpCodes
100 */
101 ENUM_BEGIN(mschapv2_opcode_names, MSCHAPV2_CHALLENGE, MSCHAPV2_FAILURE,
102 "CHALLENGE",
103 "RESPONSE",
104 "SUCCESS",
105 "FAILURE");
106 ENUM_NEXT(mschapv2_opcode_names, MSCHAPV2_CHANGE_PASSWORD, MSCHAPV2_CHANGE_PASSWORD, MSCHAPV2_FAILURE,
107 "CHANGE_PASSWORD");
108 ENUM_END(mschapv2_opcode_names, MSCHAPV2_CHANGE_PASSWORD);
109
110 /**
111 * Error codes
112 */
113 enum mschapv2_error_t
114 {
115 ERROR_RESTRICTED_LOGON_HOURS = 646,
116 ERROR_ACCT_DISABLED = 647,
117 ERROR_PASSWD_EXPIRED = 648,
118 ERROR_NO_DIALIN_PERMISSION = 649,
119 ERROR_AUTHENTICATION_FAILURE = 691,
120 ERROR_CHANGING_PASSWORD = 709,
121 };
122
123 /**
124 * Names for error codes
125 */
126 ENUM_BEGIN(mschapv2_error_names, ERROR_RESTRICTED_LOGON_HOURS, ERROR_NO_DIALIN_PERMISSION,
127 "ERROR_RESTRICTED_LOGON_HOURS",
128 "ERROR_ACCT_DISABLED",
129 "ERROR_PASSWD_EXPIRED",
130 "ERROR_NO_DIALIN_PERMISSION");
131 ENUM_NEXT(mschapv2_error_names, ERROR_AUTHENTICATION_FAILURE, ERROR_AUTHENTICATION_FAILURE, ERROR_NO_DIALIN_PERMISSION,
132 "ERROR_AUTHENTICATION_FAILURE");
133 ENUM_NEXT(mschapv2_error_names, ERROR_CHANGING_PASSWORD, ERROR_CHANGING_PASSWORD, ERROR_AUTHENTICATION_FAILURE,
134 "ERROR_CHANGING_PASSWORD");
135 ENUM_END(mschapv2_error_names, ERROR_CHANGING_PASSWORD);
136
137 /* Length of the challenge */
138 #define CHALLENGE_LEN 16
139 /* Length of the response (see eap_mschapv2_response_t) */
140 #define RESPONSE_LEN 49
141 /* Length of the authenticator response string ("S=<...>") */
142 #define AUTH_RESPONSE_LEN 42
143 /* Name we send as authenticator */
144 #define MSCHAPV2_HOST_NAME "strongSwan"
145 /* Message sent on success */
146 #define SUCCESS_MESSAGE " M=Welcome2strongSwan"
147 /* Message sent on failure */
148 #define FAILURE_MESSAGE "E=691 R=1 C="
149 /* Length of the complete failure message */
150 #define FAILURE_MESSAGE_LEN (sizeof(FAILURE_MESSAGE) + CHALLENGE_LEN * 2)
151
152 /* Number of seconds to delay retries */
153 #define RETRY_DELAY 2
154 /* Maximum number of retries */
155 #define MAX_RETRIES 2
156
157 typedef struct eap_mschapv2_header_t eap_mschapv2_header_t;
158 typedef struct eap_mschapv2_challenge_t eap_mschapv2_challenge_t;
159 typedef struct eap_mschapv2_response_t eap_mschapv2_response_t;
160
161 /**
162 * packed EAP-MS-CHAPv2 header struct
163 */
164 struct eap_mschapv2_header_t
165 {
166 /** EAP code (REQUEST/RESPONSE) */
167 u_int8_t code;
168 /** unique message identifier */
169 u_int8_t identifier;
170 /** length of whole message */
171 u_int16_t length;
172 /** EAP type */
173 u_int8_t type;
174 /** MS-CHAPv2 OpCode */
175 u_int8_t opcode;
176 /** MS-CHAPv2-ID (equals identifier) */
177 u_int8_t ms_chapv2_id;
178 /** MS-Length (defined as length - 5) */
179 u_int16_t ms_length;
180 /** packet data (determined by OpCode) */
181 u_int8_t data[];
182 }__attribute__((__packed__));
183
184 /**
185 * packed data for a MS-CHAPv2 Challenge packet
186 */
187 struct eap_mschapv2_challenge_t
188 {
189 /** Value-Size */
190 u_int8_t value_size;
191 /** Challenge */
192 u_int8_t challenge[CHALLENGE_LEN];
193 /** Name */
194 u_int8_t name[];
195 }__attribute__((__packed__));
196
197 /**
198 * packed data for a MS-CHAPv2 Response packet
199 */
200 struct eap_mschapv2_response_t
201 {
202 /** Value-Size */
203 u_int8_t value_size;
204 /** Response */
205 struct
206 {
207 /* Peer-Challenge*/
208 u_int8_t peer_challenge[CHALLENGE_LEN];
209 /* Reserved (=zero) */
210 u_int8_t peer_reserved[8];
211 /* NT-Response */
212 u_int8_t nt_response[24];
213 /* Flags (=zero) */
214 u_int8_t flags;
215 } response;
216 /** Name */
217 u_int8_t name[];
218 }__attribute__((__packed__));
219
220 /**
221 * Length of the MS-CHAPv2 header
222 */
223 #define HEADER_LEN (sizeof(eap_mschapv2_header_t))
224
225 /**
226 * Length of the header for MS-CHAPv2 success/failure packets (does not include
227 * MS-CHAPv2-ID and MS-Length, i.e. 3 octets)
228 */
229 #define SHORT_HEADER_LEN (HEADER_LEN - 3)
230
231 /**
232 * The minimum length of an MS-CHAPv2 Challenge packet (the name MUST be
233 * at least one octet)
234 */
235 #define CHALLENGE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_challenge_t))
236
237 /**
238 * The minimum length of an MS-CHAPv2 Response packet
239 */
240 #define RESPONSE_PAYLOAD_LEN (HEADER_LEN + sizeof(eap_mschapv2_response_t))
241
242
243 /**
244 * Expand a 56-bit key to a 64-bit DES key by adding parity bits (odd parity)
245 */
246 static chunk_t ExpandDESKey(chunk_t key)
247 {
248 static const u_char bitmask[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
249 int i;
250 u_char carry = 0;
251 chunk_t expanded;
252
253 /* expand the 7 octets to 8 octets */
254 expanded = chunk_alloc(8);
255 for (i = 0; i < 7; i++)
256 {
257 expanded.ptr[i] = ((key.ptr[i] & bitmask[i]) >> i) | (carry << (8 - i));
258 carry = key.ptr[i] & ~bitmask[i];
259 }
260 expanded.ptr[7] = carry << 1;
261
262 /* add parity bits to each octet */
263 for (i = 0; i < 8; i++)
264 {
265 u_char val = expanded.ptr[i];
266 val = (val ^ (val >> 4)) & 0x0f;
267 expanded.ptr[i] |= (0x9669 >> val) & 1;
268 }
269 return expanded;
270 }
271
272 /**
273 * Calculate the NT password hash (i.e. hash the (unicode) password with MD4)
274 */
275 static status_t NtPasswordHash(chunk_t password, chunk_t *password_hash)
276 {
277 hasher_t *hasher;
278 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD4);
279 if (hasher == NULL)
280 {
281 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no MD4 hasher available");
282 return FAILED;
283 }
284 if (!hasher->allocate_hash(hasher, password, password_hash))
285 {
286 hasher->destroy(hasher);
287 return FAILED;
288 }
289 hasher->destroy(hasher);
290 return SUCCESS;
291 }
292
293 /**
294 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
295 * username (without domain part)] with SHA1)
296 */
297 static status_t ChallengeHash(chunk_t peer_challenge, chunk_t server_challenge,
298 chunk_t username, chunk_t *challenge_hash)
299 {
300 chunk_t concat;
301 hasher_t *hasher;
302 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
303 if (hasher == NULL)
304 {
305 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
306 return FAILED;
307 }
308 concat = chunk_cata("ccc", peer_challenge, server_challenge, username);
309 if (!hasher->allocate_hash(hasher, concat, challenge_hash))
310 {
311 hasher->destroy(hasher);
312 return FAILED;
313 }
314 hasher->destroy(hasher);
315 /* we need only the first 8 octets */
316 challenge_hash->len = 8;
317 return SUCCESS;
318 }
319
320 /**
321 * Calculate the challenge response (i.e. expand password_hash to three DES keys
322 * and then encrypt the 8-octet challenge_hash with these keys and concatenate
323 * the results).
324 */
325 static status_t ChallengeResponse(chunk_t challenge_hash, chunk_t password_hash,
326 chunk_t *response)
327 {
328 int i;
329 crypter_t *crypter;
330 chunk_t keys[3], z_password_hash;
331 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_DES_ECB, 8);
332 if (crypter == NULL)
333 {
334 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
335 return FAILED;
336 }
337 /* prepare keys: first pad password_hash to 21 octets, these get then split
338 * into 7-octet chunks, which then get expanded into 8-octet DES keys */
339 z_password_hash = chunk_alloca(21);
340 memset(z_password_hash.ptr, 0, z_password_hash.len);
341 memcpy(z_password_hash.ptr, password_hash.ptr, password_hash.len);
342 chunk_split(z_password_hash, "mmm", 7, &keys[0], 7, &keys[1], 7, &keys[2]);
343
344 *response = chunk_alloc(24);
345 for (i = 0; i < 3; i++)
346 {
347 chunk_t expanded, encrypted;
348
349 expanded = ExpandDESKey(keys[i]);
350 if (!crypter->set_key(crypter, expanded) ||
351 !crypter->encrypt(crypter, challenge_hash, chunk_empty, &encrypted))
352 {
353 chunk_clear(&expanded);
354 crypter->destroy(crypter);
355 return FAILED;
356 }
357 memcpy(&response->ptr[i * 8], encrypted.ptr, encrypted.len);
358 chunk_clear(&encrypted);
359 chunk_clear(&expanded);
360 }
361 crypter->destroy(crypter);
362 return SUCCESS;
363 }
364
365 /**
366 * Computes the authenticator response
367 */
368 static status_t AuthenticatorResponse(chunk_t password_hash_hash,
369 chunk_t challenge_hash, chunk_t nt_response, chunk_t *response)
370 {
371 chunk_t magic1 = chunk_from_chars(
372 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
373 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
374 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
375 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
376 chunk_t magic2 = chunk_from_chars(
377 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
378 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
379 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
380 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
381 0x6E);
382 chunk_t digest = chunk_empty, concat;
383 hasher_t *hasher;
384
385 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
386 if (hasher == NULL)
387 {
388 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
389 return FAILED;
390 }
391
392 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
393 if (!hasher->allocate_hash(hasher, concat, &digest))
394 {
395 hasher->destroy(hasher);
396 return FAILED;
397 }
398 concat = chunk_cata("ccc", digest, challenge_hash, magic2);
399 if (!hasher->allocate_hash(hasher, concat, response))
400 {
401 hasher->destroy(hasher);
402 return FAILED;
403 }
404 hasher->destroy(hasher);
405 chunk_free(&digest);
406 return SUCCESS;
407 }
408
409 /**
410 * Generate the master session key according to RFC3079
411 */
412 static status_t GenerateMSK(chunk_t password_hash_hash,
413 chunk_t nt_response, chunk_t *msk)
414 {
415 chunk_t magic1 = chunk_from_chars(
416 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
417 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
418 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
419 chunk_t magic2 = chunk_from_chars(
420 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
421 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
422 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
423 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
424 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
425 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
426 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
427 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
428 0x6b, 0x65, 0x79, 0x2e);
429 chunk_t magic3 = chunk_from_chars(
430 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
431 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
432 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
433 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
434 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
435 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
436 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
437 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
438 0x6b, 0x65, 0x79, 0x2e);
439 chunk_t shapad1 = chunk_from_chars(
440 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
444 chunk_t shapad2 = chunk_from_chars(
445 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
446 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
447 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
448 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);
449 chunk_t keypad = chunk_from_chars(
450 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
452 char master_key[HASH_SIZE_SHA1];
453 char master_receive_key[HASH_SIZE_SHA1], master_send_key[HASH_SIZE_SHA1];
454 chunk_t concat, master;
455 hasher_t *hasher;
456
457 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
458 if (hasher == NULL)
459 {
460 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
461 return FAILED;
462 }
463
464 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
465 if (!hasher->get_hash(hasher, concat, master_key))
466 {
467 hasher->destroy(hasher);
468 return FAILED;
469 }
470 master = chunk_create(master_key, 16);
471 concat = chunk_cata("cccc", master, shapad1, magic2, shapad2);
472 if (!hasher->get_hash(hasher, concat, master_receive_key))
473 {
474 hasher->destroy(hasher);
475 return FAILED;
476 }
477 concat = chunk_cata("cccc", master, shapad1, magic3, shapad2);
478 if (!hasher->get_hash(hasher, concat, master_send_key))
479 {
480 hasher->destroy(hasher);
481 return FAILED;
482 }
483
484 *msk = chunk_cat("cccc", chunk_create(master_receive_key, 16),
485 chunk_create(master_send_key, 16), keypad, keypad);
486
487 hasher->destroy(hasher);
488 return SUCCESS;
489 }
490
491 static status_t GenerateStuff(private_eap_mschapv2_t *this,
492 chunk_t server_challenge, chunk_t peer_challenge,
493 chunk_t username, chunk_t nt_hash)
494 {
495 status_t status = FAILED;
496 chunk_t nt_hash_hash = chunk_empty, challenge_hash = chunk_empty;
497
498 if (NtPasswordHash(nt_hash, &nt_hash_hash) != SUCCESS)
499 {
500 goto error;
501 }
502 if (ChallengeHash(peer_challenge, server_challenge, username,
503 &challenge_hash) != SUCCESS)
504 {
505 goto error;
506 }
507 if (ChallengeResponse(challenge_hash, nt_hash,
508 &this->nt_response) != SUCCESS)
509 {
510 goto error;
511 }
512 if (AuthenticatorResponse(nt_hash_hash, challenge_hash,
513 this->nt_response, &this->auth_response) != SUCCESS)
514 {
515 goto error;
516 }
517 if (GenerateMSK(nt_hash_hash, this->nt_response, &this->msk) != SUCCESS)
518 {
519 goto error;
520 }
521
522 status = SUCCESS;
523
524 error:
525 chunk_free(&nt_hash_hash);
526 chunk_free(&challenge_hash);
527 return status;
528 }
529
530 /**
531 * Converts an ASCII string into a UTF-16 (little-endian) string
532 */
533 static chunk_t ascii_to_unicode(chunk_t ascii)
534 {
535 int i;
536 chunk_t unicode = chunk_alloc(ascii.len * 2);
537 for (i = 0; i < ascii.len; i++)
538 {
539 unicode.ptr[i * 2] = ascii.ptr[i];
540 unicode.ptr[i * 2 + 1] = 0;
541 }
542 return unicode;
543 }
544
545 /**
546 * sanitize a string for printing
547 */
548 static char* sanitize(char *str)
549 {
550 char *pos = str;
551
552 while (pos && *pos)
553 {
554 if (!isprint(*pos))
555 {
556 *pos = '?';
557 }
558 pos++;
559 }
560 return str;
561 }
562
563 /**
564 * Returns a chunk of just the username part of the given user identity.
565 * Note: the chunk points to internal data of the given chunk
566 */
567 static chunk_t extract_username(chunk_t id)
568 {
569 char *has_domain;
570
571 has_domain = (char*)memchr(id.ptr, '\\', id.len);
572 if (has_domain)
573 {
574 int len;
575 has_domain++; /* skip the backslash */
576 len = id.len - ((u_char*)has_domain - id.ptr);
577 return len > 0 ? chunk_create(has_domain, len) : chunk_empty;
578 }
579 return id;
580 }
581
582 /**
583 * Set the ms_length field using aligned write
584 */
585 static void set_ms_length(eap_mschapv2_header_t *eap, u_int16_t len)
586 {
587 len = htons(len - 5);
588 memcpy(&eap->ms_length, &len, sizeof(u_int16_t));
589 }
590
591 METHOD(eap_method_t, initiate_peer, status_t,
592 private_eap_mschapv2_t *this, eap_payload_t **out)
593 {
594 /* peer never initiates */
595 return FAILED;
596 }
597
598 METHOD(eap_method_t, initiate_server, status_t,
599 private_eap_mschapv2_t *this, eap_payload_t **out)
600 {
601 rng_t *rng;
602 eap_mschapv2_header_t *eap;
603 eap_mschapv2_challenge_t *cha;
604 const char *name = MSCHAPV2_HOST_NAME;
605 u_int16_t len = CHALLENGE_PAYLOAD_LEN + sizeof(MSCHAPV2_HOST_NAME) - 1;
606
607 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
608 if (!rng || !rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge))
609 {
610 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no challenge");
611 DESTROY_IF(rng);
612 return FAILED;
613 }
614 rng->destroy(rng);
615
616 eap = alloca(len);
617 eap->code = EAP_REQUEST;
618 eap->identifier = this->identifier;
619 eap->length = htons(len);
620 eap->type = EAP_MSCHAPV2;
621 eap->opcode = MSCHAPV2_CHALLENGE;
622 eap->ms_chapv2_id = this->mschapv2id;
623 set_ms_length(eap, len);
624
625 cha = (eap_mschapv2_challenge_t*)eap->data;
626 cha->value_size = CHALLENGE_LEN;
627 memcpy(cha->challenge, this->challenge.ptr, this->challenge.len);
628 memcpy(cha->name, name, sizeof(MSCHAPV2_HOST_NAME) - 1);
629
630 *out = eap_payload_create_data(chunk_create((void*) eap, len));
631 return NEED_MORE;
632 }
633
634 static bool get_nt_hash(private_eap_mschapv2_t *this, identification_t *me,
635 identification_t *other, chunk_t *nt_hash)
636 {
637 shared_key_t *shared;
638 chunk_t password;
639
640 /* try to find a stored NT_HASH first */
641 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_NT_HASH, me, other);
642 if (shared )
643 {
644 *nt_hash = chunk_clone(shared->get_key(shared));
645 shared->destroy(shared);
646 return TRUE;
647 }
648
649 /* fallback to plaintext password */
650 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, me, other);
651 if (shared)
652 {
653 password = ascii_to_unicode(shared->get_key(shared));
654 shared->destroy(shared);
655
656 if (NtPasswordHash(password, nt_hash) == SUCCESS)
657 {
658 chunk_clear(&password);
659 return TRUE;
660 }
661 chunk_clear(&password);
662 }
663 return FALSE;
664 }
665
666 /**
667 * Process MS-CHAPv2 Challenge Requests
668 */
669 static status_t process_peer_challenge(private_eap_mschapv2_t *this,
670 eap_payload_t *in, eap_payload_t **out)
671 {
672 rng_t *rng;
673 eap_mschapv2_header_t *eap;
674 eap_mschapv2_challenge_t *cha;
675 eap_mschapv2_response_t *res;
676 chunk_t data, peer_challenge, userid, username, nt_hash;
677 u_int16_t len = RESPONSE_PAYLOAD_LEN;
678
679 data = in->get_data(in);
680 eap = (eap_mschapv2_header_t*)data.ptr;
681
682 /* the name MUST be at least one octet long */
683 if (data.len < CHALLENGE_PAYLOAD_LEN + 1)
684 {
685 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
686 return FAILED;
687 }
688
689 cha = (eap_mschapv2_challenge_t*)eap->data;
690
691 if (cha->value_size != CHALLENGE_LEN)
692 {
693 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
694 "invalid challenge size");
695 return FAILED;
696 }
697
698 this->mschapv2id = eap->ms_chapv2_id;
699 this->challenge = chunk_clone(chunk_create(cha->challenge, CHALLENGE_LEN));
700
701 peer_challenge = chunk_alloca(CHALLENGE_LEN);
702 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
703 if (!rng || !rng->get_bytes(rng, CHALLENGE_LEN, peer_challenge.ptr))
704 {
705 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, allocating challenge failed");
706 DESTROY_IF(rng);
707 return FAILED;
708 }
709 rng->destroy(rng);
710
711 if (!get_nt_hash(this, this->peer, this->server, &nt_hash))
712 {
713 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
714 this->server, this->peer);
715 return NOT_FOUND;
716 }
717
718 /* we transmit the whole user identity (including the domain part) but
719 * only use the user part when calculating the challenge hash */
720 userid = this->peer->get_encoding(this->peer);
721 len += userid.len;
722 username = extract_username(userid);
723
724 if (GenerateStuff(this, this->challenge, peer_challenge,
725 username, nt_hash) != SUCCESS)
726 {
727 DBG1(DBG_IKE, "EAP-MS-CHAPv2 generating NT-Response failed");
728 chunk_clear(&nt_hash);
729 return FAILED;
730 }
731 chunk_clear(&nt_hash);
732
733 eap = alloca(len);
734 eap->code = EAP_RESPONSE;
735 eap->identifier = this->identifier;
736 eap->length = htons(len);
737 eap->type = EAP_MSCHAPV2;
738 eap->opcode = MSCHAPV2_RESPONSE;
739 eap->ms_chapv2_id = this->mschapv2id;
740 set_ms_length(eap, len);
741
742 res = (eap_mschapv2_response_t*)eap->data;
743 res->value_size = RESPONSE_LEN;
744 memset(&res->response, 0, RESPONSE_LEN);
745 memcpy(res->response.peer_challenge, peer_challenge.ptr, peer_challenge.len);
746 memcpy(res->response.nt_response, this->nt_response.ptr, this->nt_response.len);
747 memcpy(res->name, userid.ptr, userid.len);
748
749 *out = eap_payload_create_data(chunk_create((void*) eap, len));
750 return NEED_MORE;
751 }
752
753 /**
754 * Process MS-CHAPv2 Success Requests
755 */
756 static status_t process_peer_success(private_eap_mschapv2_t *this,
757 eap_payload_t *in, eap_payload_t **out)
758 {
759 status_t status = FAILED;
760 enumerator_t *enumerator;
761 eap_mschapv2_header_t *eap;
762 chunk_t data, auth_string = chunk_empty;
763 char *message, *token, *msg = NULL;
764 int message_len;
765 u_int16_t len = SHORT_HEADER_LEN;
766
767 data = in->get_data(in);
768 eap = (eap_mschapv2_header_t*)data.ptr;
769
770 if (data.len < AUTH_RESPONSE_LEN)
771 {
772 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
773 return FAILED;
774 }
775
776 message_len = data.len - HEADER_LEN;
777 message = malloc(message_len + 1);
778 memcpy(message, eap->data, message_len);
779 message[message_len] = '\0';
780
781 /* S=<auth_string> M=<msg> */
782 enumerator = enumerator_create_token(message, " ", " ");
783 while (enumerator->enumerate(enumerator, &token))
784 {
785 if (strneq(token, "S=", 2))
786 {
787 chunk_t hex;
788 token += 2;
789 if (strlen(token) != AUTH_RESPONSE_LEN - 2)
790 {
791 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
792 "invalid auth string");
793 goto error;
794 }
795 hex = chunk_create(token, AUTH_RESPONSE_LEN - 2);
796 auth_string = chunk_from_hex(hex, NULL);
797 }
798 else if (strneq(token, "M=", 2))
799 {
800 token += 2;
801 msg = strdup(token);
802 }
803 }
804 enumerator->destroy(enumerator);
805
806 if (auth_string.ptr == NULL)
807 {
808 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
809 "auth string missing");
810 goto error;
811 }
812
813 if (!chunk_equals(this->auth_response, auth_string))
814 {
815 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
816 goto error;
817 }
818
819 DBG1(DBG_IKE, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg));
820
821 eap = alloca(len);
822 eap->code = EAP_RESPONSE;
823 eap->identifier = this->identifier;
824 eap->length = htons(len);
825 eap->type = EAP_MSCHAPV2;
826 eap->opcode = MSCHAPV2_SUCCESS;
827
828 *out = eap_payload_create_data(chunk_create((void*) eap, len));
829 status = NEED_MORE;
830
831 error:
832 chunk_free(&auth_string);
833 free(message);
834 free(msg);
835 return status;
836 }
837
838 static status_t process_peer_failure(private_eap_mschapv2_t *this,
839 eap_payload_t *in, eap_payload_t **out)
840 {
841 status_t status = FAILED;
842 enumerator_t *enumerator;
843 eap_mschapv2_header_t *eap;
844 chunk_t data;
845 char *message, *token, *msg = NULL;
846 int message_len, error = 0;
847 chunk_t challenge = chunk_empty;
848
849 data = in->get_data(in);
850 eap = (eap_mschapv2_header_t*)data.ptr;
851
852 if (data.len < 3) /* we want at least an error code: E=e */
853 {
854 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
855 return FAILED;
856 }
857
858 message_len = data.len - HEADER_LEN;
859 message = malloc(message_len + 1);
860 memcpy(message, eap->data, message_len);
861 message[message_len] = '\0';
862
863 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
864 enumerator = enumerator_create_token(message, " ", " ");
865 while (enumerator->enumerate(enumerator, &token))
866 {
867 if (strneq(token, "E=", 2))
868 {
869 token += 2;
870 error = atoi(token);
871 }
872 else if (strneq(token, "R=", 2))
873 {
874 /* ignore retriable */
875 }
876 else if (strneq(token, "C=", 2))
877 {
878 chunk_t hex;
879 token += 2;
880 if (strlen(token) != 2 * CHALLENGE_LEN)
881 {
882 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message:"
883 "invalid challenge");
884 goto error;
885 }
886 hex = chunk_create(token, 2 * CHALLENGE_LEN);
887 challenge = chunk_from_hex(hex, NULL);
888 }
889 else if (strneq(token, "V=", 2))
890 {
891 /* ignore version */
892 }
893 else if (strneq(token, "M=", 2))
894 {
895 token += 2;
896 msg = strdup(token);
897 }
898 }
899 enumerator->destroy(enumerator);
900
901 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed with error %N: '%s'",
902 mschapv2_error_names, error, sanitize(msg));
903
904 /**
905 * at this point, if the error is retriable, we MAY retry the authentication
906 * or MAY send a Change Password packet.
907 *
908 * if the error is not retriable (or if we do neither of the above), we
909 * SHOULD send a Failure Response packet.
910 * windows clients don't do that, and since windows server 2008 r2 behaves
911 * pretty odd if we do send a Failure Response, we just don't send one
912 * either. windows 7 actually sends a delete notify (which, according to the
913 * logs, results in an error on windows server 2008 r2).
914 *
915 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
916 * a disabled account but returns the windows error code in a notify payload
917 * of type 12345.
918 */
919
920 status = FAILED;
921
922 error:
923 chunk_free(&challenge);
924 free(message);
925 free(msg);
926 return status;
927 }
928
929 METHOD(eap_method_t, process_peer, status_t,
930 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
931 {
932 chunk_t data;
933 eap_mschapv2_header_t *eap;
934
935 this->identifier = in->get_identifier(in);
936 data = in->get_data(in);
937 if (data.len < SHORT_HEADER_LEN)
938 {
939 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message");
940 return FAILED;
941 }
942
943 eap = (eap_mschapv2_header_t*)data.ptr;
944
945 switch (eap->opcode)
946 {
947 case MSCHAPV2_CHALLENGE:
948 {
949 return process_peer_challenge(this, in, out);
950 }
951 case MSCHAPV2_SUCCESS:
952 {
953 return process_peer_success(this, in, out);
954 }
955 case MSCHAPV2_FAILURE:
956 {
957 return process_peer_failure(this, in, out);
958 }
959 default:
960 {
961 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
962 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
963 break;
964 }
965 }
966 return FAILED;
967 }
968
969 /**
970 * Handles retries on the server
971 */
972 static status_t process_server_retry(private_eap_mschapv2_t *this,
973 eap_payload_t **out)
974 {
975 eap_mschapv2_header_t *eap;
976 rng_t *rng;
977 chunk_t hex;
978 char msg[FAILURE_MESSAGE_LEN];
979 u_int16_t len = HEADER_LEN + FAILURE_MESSAGE_LEN - 1; /* no null byte */
980
981 if (++this->retries > MAX_RETRIES)
982 {
983 /* we MAY send a Failure Request with R=0, but windows 7 does not
984 * really like that and does not respond with a Failure Response.
985 * so, to clean up our state we just fail with an EAP-Failure.
986 * this gives an unknown error on the windows side, but is also fine
987 * with the standard. */
988 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed: "
989 "maximum number of retries reached");
990 return FAILED;
991 }
992
993 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries);
994
995 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
996 if (!rng || !rng->get_bytes(rng, CHALLENGE_LEN, this->challenge.ptr))
997 {
998 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, allocating challenge failed");
999 DESTROY_IF(rng);
1000 return FAILED;
1001 }
1002 rng->destroy(rng);
1003
1004 chunk_free(&this->nt_response);
1005 chunk_free(&this->auth_response);
1006 chunk_free(&this->msk);
1007
1008 eap = alloca(len);
1009 eap->code = EAP_REQUEST;
1010 eap->identifier = ++this->identifier;
1011 eap->length = htons(len);
1012 eap->type = EAP_MSCHAPV2;
1013 eap->opcode = MSCHAPV2_FAILURE;
1014 eap->ms_chapv2_id = this->mschapv2id++; /* increase for each retry */
1015 set_ms_length(eap, len);
1016
1017 hex = chunk_to_hex(this->challenge, NULL, TRUE);
1018 snprintf(msg, FAILURE_MESSAGE_LEN, "%s%s", FAILURE_MESSAGE, hex.ptr);
1019 chunk_free(&hex);
1020 memcpy(eap->data, msg, FAILURE_MESSAGE_LEN - 1); /* no null byte */
1021 *out = eap_payload_create_data(chunk_create((void*) eap, len));
1022
1023 /* delay the response for some time to make brute-force attacks harder */
1024 sleep(RETRY_DELAY);
1025
1026 return NEED_MORE;
1027 }
1028
1029 /**
1030 * Process MS-CHAPv2 Response response packets
1031 */
1032 static status_t process_server_response(private_eap_mschapv2_t *this,
1033 eap_payload_t *in, eap_payload_t **out)
1034 {
1035 eap_mschapv2_header_t *eap;
1036 eap_mschapv2_response_t *res;
1037 chunk_t data, peer_challenge, username, nt_hash;
1038 identification_t *userid;
1039 int name_len;
1040 char buf[256];
1041
1042 data = in->get_data(in);
1043 eap = (eap_mschapv2_header_t*)data.ptr;
1044
1045 if (data.len < RESPONSE_PAYLOAD_LEN)
1046 {
1047 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1048 return FAILED;
1049 }
1050
1051 res = (eap_mschapv2_response_t*)eap->data;
1052 peer_challenge = chunk_create(res->response.peer_challenge, CHALLENGE_LEN);
1053
1054 name_len = min(data.len - RESPONSE_PAYLOAD_LEN, 255);
1055 snprintf(buf, sizeof(buf), "%.*s", name_len, res->name);
1056 userid = identification_create_from_string(buf);
1057 DBG2(DBG_IKE, "EAP-MS-CHAPv2 username: '%Y'", userid);
1058 /* userid can only be destroyed after the last use of username */
1059 username = extract_username(userid->get_encoding(userid));
1060
1061 if (!get_nt_hash(this, this->server, userid, &nt_hash))
1062 {
1063 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
1064 this->server, userid);
1065 /* FIXME: windows 7 always sends the username that is first entered in
1066 * the username box, even, if the user changes it during retries (probably
1067 * to keep consistent with the EAP-Identity).
1068 * thus, we could actually fail here, because retries do not make much
1069 * sense. on the other hand, an attacker could guess usernames, if the
1070 * error messages were different. */
1071 userid->destroy(userid);
1072 return process_server_retry(this, out);
1073 }
1074
1075 if (GenerateStuff(this, this->challenge, peer_challenge,
1076 username, nt_hash) != SUCCESS)
1077 {
1078 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
1079 userid->destroy(userid);
1080 chunk_clear(&nt_hash);
1081 return FAILED;
1082 }
1083 userid->destroy(userid);
1084 chunk_clear(&nt_hash);
1085
1086 if (memeq(res->response.nt_response, this->nt_response.ptr,
1087 this->nt_response.len))
1088 {
1089 chunk_t hex;
1090 char msg[AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE)];
1091 u_int16_t len = HEADER_LEN + AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE);
1092
1093 eap = alloca(len);
1094 eap->code = EAP_REQUEST;
1095 eap->identifier = ++this->identifier;
1096 eap->length = htons(len);
1097 eap->type = EAP_MSCHAPV2;
1098 eap->opcode = MSCHAPV2_SUCCESS;
1099 eap->ms_chapv2_id = this->mschapv2id;
1100 set_ms_length(eap, len);
1101
1102 hex = chunk_to_hex(this->auth_response, NULL, TRUE);
1103 snprintf(msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE),
1104 "S=%s%s", hex.ptr, SUCCESS_MESSAGE);
1105 chunk_free(&hex);
1106 memcpy(eap->data, msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE));
1107 *out = eap_payload_create_data(chunk_create((void*) eap, len));
1108 return NEED_MORE;
1109 }
1110
1111 return process_server_retry(this, out);
1112 }
1113
1114 METHOD(eap_method_t, process_server, status_t,
1115 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
1116 {
1117 eap_mschapv2_header_t *eap;
1118 chunk_t data;
1119
1120 if (this->identifier != in->get_identifier(in))
1121 {
1122 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
1123 "unexpected identifier");
1124 return FAILED;
1125 }
1126
1127 data = in->get_data(in);
1128 if (data.len < SHORT_HEADER_LEN)
1129 {
1130 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1131 return FAILED;
1132 }
1133
1134 eap = (eap_mschapv2_header_t*)data.ptr;
1135
1136 switch (eap->opcode)
1137 {
1138 case MSCHAPV2_RESPONSE:
1139 {
1140 return process_server_response(this, in, out);
1141 }
1142 case MSCHAPV2_SUCCESS:
1143 {
1144 return SUCCESS;
1145 }
1146 case MSCHAPV2_FAILURE:
1147 {
1148 return FAILED;
1149 }
1150 default:
1151 {
1152 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
1153 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1154 break;
1155 }
1156 }
1157 return FAILED;
1158 }
1159
1160 METHOD(eap_method_t, get_type, eap_type_t,
1161 private_eap_mschapv2_t *this, u_int32_t *vendor)
1162 {
1163 *vendor = 0;
1164 return EAP_MSCHAPV2;
1165 }
1166
1167 METHOD(eap_method_t, get_msk, status_t,
1168 private_eap_mschapv2_t *this, chunk_t *msk)
1169 {
1170 if (this->msk.ptr)
1171 {
1172 *msk = this->msk;
1173 return SUCCESS;
1174 }
1175 return FAILED;
1176 }
1177
1178 METHOD(eap_method_t, get_identifier, u_int8_t,
1179 private_eap_mschapv2_t *this)
1180 {
1181 return this->identifier;
1182 }
1183
1184 METHOD(eap_method_t, set_identifier, void,
1185 private_eap_mschapv2_t *this, u_int8_t identifier)
1186 {
1187 this->identifier = identifier;
1188 }
1189
1190 METHOD(eap_method_t, is_mutual, bool,
1191 private_eap_mschapv2_t *this)
1192 {
1193 return FALSE;
1194 }
1195
1196 METHOD(eap_method_t, destroy, void,
1197 private_eap_mschapv2_t *this)
1198 {
1199 this->peer->destroy(this->peer);
1200 this->server->destroy(this->server);
1201 chunk_free(&this->challenge);
1202 chunk_free(&this->nt_response);
1203 chunk_free(&this->auth_response);
1204 chunk_free(&this->msk);
1205 free(this);
1206 }
1207
1208 /**
1209 * Generic constructor
1210 */
1211 static private_eap_mschapv2_t *eap_mschapv2_create_generic(identification_t *server, identification_t *peer)
1212 {
1213 private_eap_mschapv2_t *this;
1214
1215 INIT(this,
1216 .public = {
1217 .eap_method_interface = {
1218 .get_type = _get_type,
1219 .is_mutual = _is_mutual,
1220 .get_msk = _get_msk,
1221 .get_identifier = _get_identifier,
1222 .set_identifier = _set_identifier,
1223 .destroy = _destroy,
1224 },
1225 },
1226 .peer = peer->clone(peer),
1227 .server = server->clone(server),
1228 );
1229
1230 return this;
1231 }
1232
1233 /*
1234 * see header
1235 */
1236 eap_mschapv2_t *eap_mschapv2_create_server(identification_t *server, identification_t *peer)
1237 {
1238 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1239
1240 this->public.eap_method_interface.initiate = _initiate_server;
1241 this->public.eap_method_interface.process = _process_server;
1242
1243 /* generate a non-zero identifier */
1244 do
1245 {
1246 this->identifier = random();
1247 } while (!this->identifier);
1248
1249 this->mschapv2id = this->identifier;
1250
1251 return &this->public;
1252 }
1253
1254 /*
1255 * see header
1256 */
1257 eap_mschapv2_t *eap_mschapv2_create_peer(identification_t *server, identification_t *peer)
1258 {
1259 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1260
1261 this->public.eap_method_interface.initiate = _initiate_peer;
1262 this->public.eap_method_interface.process = _process_peer;
1263
1264 return &this->public;
1265 }
1266