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