Migrated eap_mschapv2 plugin to INIT/METHOD macros
[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 <utils/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 hasher->allocate_hash(hasher, password, password_hash);
285 hasher->destroy(hasher);
286 return SUCCESS;
287 }
288
289 /**
290 * Calculate the challenge hash (i.e. hash [peer_challenge | server_challenge |
291 * username (without domain part)] with SHA1)
292 */
293 static status_t ChallengeHash(chunk_t peer_challenge, chunk_t server_challenge,
294 chunk_t username, chunk_t *challenge_hash)
295 {
296 chunk_t concat;
297 hasher_t *hasher;
298 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
299 if (hasher == NULL)
300 {
301 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
302 return FAILED;
303 }
304 concat = chunk_cata("ccc", peer_challenge, server_challenge, username);
305 hasher->allocate_hash(hasher, concat, challenge_hash);
306 hasher->destroy(hasher);
307 /* we need only the first 8 octets */
308 challenge_hash->len = 8;
309 return SUCCESS;
310 }
311
312 /**
313 * Calculate the challenge response (i.e. expand password_hash to three DES keys
314 * and then encrypt the 8-octet challenge_hash with these keys and concatenate
315 * the results).
316 */
317 static status_t ChallengeResponse(chunk_t challenge_hash, chunk_t password_hash,
318 chunk_t *response)
319 {
320 int i;
321 crypter_t *crypter;
322 chunk_t keys[3], z_password_hash;
323 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_DES_ECB, 8);
324 if (crypter == NULL)
325 {
326 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, DES-ECB not supported");
327 return FAILED;
328 }
329 /* prepare keys: first pad password_hash to 21 octets, these get then split
330 * into 7-octet chunks, which then get expanded into 8-octet DES keys */
331 z_password_hash = chunk_alloca(21);
332 memset(z_password_hash.ptr, 0, z_password_hash.len);
333 memcpy(z_password_hash.ptr, password_hash.ptr, password_hash.len);
334 chunk_split(z_password_hash, "mmm", 7, &keys[0], 7, &keys[1], 7, &keys[2]);
335
336 *response = chunk_alloc(24);
337 for (i = 0; i < 3; i++)
338 {
339 chunk_t expanded, encrypted;
340 expanded = ExpandDESKey(keys[i]);
341 crypter->set_key(crypter, expanded);
342 crypter->encrypt(crypter, challenge_hash, chunk_empty, &encrypted);
343 memcpy(&response->ptr[i * 8], encrypted.ptr, encrypted.len);
344 chunk_clear(&encrypted);
345 chunk_clear(&expanded);
346 }
347 crypter->destroy(crypter);
348 return SUCCESS;
349 }
350
351 /**
352 * Computes the authenticator response
353 */
354 static status_t AuthenticatorResponse(chunk_t password_hash_hash,
355 chunk_t challenge_hash, chunk_t nt_response, chunk_t *response)
356 {
357 chunk_t magic1 = chunk_from_chars(
358 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
359 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
360 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
361 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
362 chunk_t magic2 = chunk_from_chars(
363 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
364 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
365 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
366 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
367 0x6E);
368 chunk_t digest = chunk_empty, concat;
369 hasher_t *hasher;
370
371 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
372 if (hasher == NULL)
373 {
374 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
375 return FAILED;
376 }
377
378 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
379 hasher->allocate_hash(hasher, concat, &digest);
380 concat = chunk_cata("ccc", digest, challenge_hash, magic2);
381 hasher->allocate_hash(hasher, concat, response);
382
383 hasher->destroy(hasher);
384 chunk_free(&digest);
385 return SUCCESS;
386 }
387
388 /**
389 * Generate the master session key according to RFC3079
390 */
391 static status_t GenerateMSK(chunk_t password_hash_hash,
392 chunk_t nt_response, chunk_t *msk)
393 {
394 chunk_t magic1 = chunk_from_chars(
395 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
396 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
397 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
398 chunk_t magic2 = chunk_from_chars(
399 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
400 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
401 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
402 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
403 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
404 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
405 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
406 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
407 0x6b, 0x65, 0x79, 0x2e);
408 chunk_t magic3 = chunk_from_chars(
409 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
410 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
411 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
412 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
413 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
414 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
415 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
416 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
417 0x6b, 0x65, 0x79, 0x2e);
418 chunk_t shapad1 = chunk_from_chars(
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
423 chunk_t shapad2 = chunk_from_chars(
424 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
425 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
426 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
427 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);
428 chunk_t keypad = chunk_from_chars(
429 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
431 chunk_t concat, master_key, master_receive_key, master_send_key;
432 hasher_t *hasher;
433
434 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
435 if (hasher == NULL)
436 {
437 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, SHA1 not supported");
438 return FAILED;
439 }
440
441 concat = chunk_cata("ccc", password_hash_hash, nt_response, magic1);
442 hasher->allocate_hash(hasher, concat, &master_key);
443 master_key.len = 16;
444
445 concat = chunk_cata("cccc", master_key, shapad1, magic2, shapad2);
446 hasher->allocate_hash(hasher, concat, &master_receive_key);
447 master_receive_key.len = 16;
448
449 concat = chunk_cata("cccc", master_key, shapad1, magic3, shapad2);
450 hasher->allocate_hash(hasher, concat, &master_send_key);
451 master_send_key.len = 16;
452
453 *msk = chunk_cat("cccc", master_receive_key, master_send_key, keypad, keypad);
454
455 hasher->destroy(hasher);
456 chunk_free(&master_key);
457 chunk_free(&master_receive_key);
458 chunk_free(&master_send_key);
459 return SUCCESS;
460 }
461
462 static status_t GenerateStuff(private_eap_mschapv2_t *this,
463 chunk_t server_challenge, chunk_t peer_challenge,
464 chunk_t username, chunk_t nt_hash)
465 {
466 status_t status = FAILED;
467 chunk_t nt_hash_hash = chunk_empty, challenge_hash = chunk_empty;
468
469 if (NtPasswordHash(nt_hash, &nt_hash_hash) != SUCCESS)
470 {
471 goto error;
472 }
473 if (ChallengeHash(peer_challenge, server_challenge, username,
474 &challenge_hash) != SUCCESS)
475 {
476 goto error;
477 }
478 if (ChallengeResponse(challenge_hash, nt_hash,
479 &this->nt_response) != SUCCESS)
480 {
481 goto error;
482 }
483 if (AuthenticatorResponse(nt_hash_hash, challenge_hash,
484 this->nt_response, &this->auth_response) != SUCCESS)
485 {
486 goto error;
487 }
488 if (GenerateMSK(nt_hash_hash, this->nt_response, &this->msk) != SUCCESS)
489 {
490 goto error;
491 }
492
493 status = SUCCESS;
494
495 error:
496 chunk_free(&nt_hash_hash);
497 chunk_free(&challenge_hash);
498 return status;
499 }
500
501 /**
502 * Converts an ASCII string into a UTF-16 (little-endian) string
503 */
504 static chunk_t ascii_to_unicode(chunk_t ascii)
505 {
506 int i;
507 chunk_t unicode = chunk_alloc(ascii.len * 2);
508 for (i = 0; i < ascii.len; i++)
509 {
510 unicode.ptr[i * 2] = ascii.ptr[i];
511 unicode.ptr[i * 2 + 1] = 0;
512 }
513 return unicode;
514 }
515
516 /**
517 * sanitize a string for printing
518 */
519 static char* sanitize(char *str)
520 {
521 char *pos = str;
522
523 while (pos && *pos)
524 {
525 if (!isprint(*pos))
526 {
527 *pos = '?';
528 }
529 pos++;
530 }
531 return str;
532 }
533
534 /**
535 * Returns a chunk of just the username part of the given user identity.
536 * Note: the chunk points to internal data of the identification.
537 */
538 static chunk_t extract_username(identification_t* identification)
539 {
540 char *has_domain;
541 chunk_t id;
542 id = identification->get_encoding(identification);
543 has_domain = (char*)memchr(id.ptr, '\\', id.len);
544 if (has_domain)
545 {
546 int len;
547 has_domain++; /* skip the backslash */
548 len = id.len - ((u_char*)has_domain - id.ptr);
549 return len > 0 ? chunk_create(has_domain, len) : chunk_empty;
550 }
551 return id;
552 }
553
554 /**
555 * Set the ms_length field using aligned write
556 */
557 static void set_ms_length(eap_mschapv2_header_t *eap, u_int16_t len)
558 {
559 len = htons(len - 5);
560 memcpy(&eap->ms_length, &len, sizeof(u_int16_t));
561 }
562
563 METHOD(eap_method_t, initiate_peer, status_t,
564 private_eap_mschapv2_t *this, eap_payload_t **out)
565 {
566 /* peer never initiates */
567 return FAILED;
568 }
569
570 METHOD(eap_method_t, initiate_server, status_t,
571 private_eap_mschapv2_t *this, eap_payload_t **out)
572 {
573 rng_t *rng;
574 eap_mschapv2_header_t *eap;
575 eap_mschapv2_challenge_t *cha;
576 const char *name = MSCHAPV2_HOST_NAME;
577 u_int16_t len = CHALLENGE_PAYLOAD_LEN + sizeof(MSCHAPV2_HOST_NAME) - 1;
578
579 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
580 if (!rng)
581 {
582 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no RNG");
583 return FAILED;
584 }
585 rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge);
586 rng->destroy(rng);
587
588 eap = alloca(len);
589 eap->code = EAP_REQUEST;
590 eap->identifier = this->identifier;
591 eap->length = htons(len);
592 eap->type = EAP_MSCHAPV2;
593 eap->opcode = MSCHAPV2_CHALLENGE;
594 eap->ms_chapv2_id = this->mschapv2id;
595 set_ms_length(eap, len);
596
597 cha = (eap_mschapv2_challenge_t*)eap->data;
598 cha->value_size = CHALLENGE_LEN;
599 memcpy(cha->challenge, this->challenge.ptr, this->challenge.len);
600 memcpy(cha->name, name, sizeof(MSCHAPV2_HOST_NAME) - 1);
601
602 *out = eap_payload_create_data(chunk_create((void*) eap, len));
603 return NEED_MORE;
604 }
605
606 static bool get_nt_hash(private_eap_mschapv2_t *this, identification_t *me,
607 identification_t *other, chunk_t *nt_hash)
608 {
609 shared_key_t *shared;
610 chunk_t password;
611
612 /* try to find a stored NT_HASH first */
613 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_NT_HASH, me, other);
614 if (shared )
615 {
616 *nt_hash = chunk_clone(shared->get_key(shared));
617 shared->destroy(shared);
618 return TRUE;
619 }
620
621 /* fallback to plaintext password */
622 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, me, other);
623 if (shared)
624 {
625 password = ascii_to_unicode(shared->get_key(shared));
626 shared->destroy(shared);
627
628 if (NtPasswordHash(password, nt_hash) == SUCCESS)
629 {
630 chunk_clear(&password);
631 return TRUE;
632 }
633 chunk_clear(&password);
634 }
635 return FALSE;
636 }
637
638 /**
639 * Process MS-CHAPv2 Challenge Requests
640 */
641 static status_t process_peer_challenge(private_eap_mschapv2_t *this,
642 eap_payload_t *in, eap_payload_t **out)
643 {
644 rng_t *rng;
645 eap_mschapv2_header_t *eap;
646 eap_mschapv2_challenge_t *cha;
647 eap_mschapv2_response_t *res;
648 chunk_t data, peer_challenge, username, nt_hash;
649 u_int16_t len = RESPONSE_PAYLOAD_LEN;
650
651 data = in->get_data(in);
652 eap = (eap_mschapv2_header_t*)data.ptr;
653
654 /* the name MUST be at least one octet long */
655 if (data.len < CHALLENGE_PAYLOAD_LEN + 1)
656 {
657 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
658 return FAILED;
659 }
660
661 cha = (eap_mschapv2_challenge_t*)eap->data;
662
663 if (cha->value_size != CHALLENGE_LEN)
664 {
665 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
666 "invalid challenge size");
667 return FAILED;
668 }
669
670 this->mschapv2id = eap->ms_chapv2_id;
671 this->challenge = chunk_clone(chunk_create(cha->challenge, CHALLENGE_LEN));
672
673 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
674 if (!rng)
675 {
676 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no RNG");
677 return FAILED;
678 }
679 peer_challenge = chunk_alloca(CHALLENGE_LEN);
680 rng->get_bytes(rng, CHALLENGE_LEN, peer_challenge.ptr);
681 rng->destroy(rng);
682
683 if (!get_nt_hash(this, this->peer, this->server, &nt_hash))
684 {
685 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
686 this->server, this->peer);
687 return NOT_FOUND;
688 }
689
690 username = extract_username(this->peer);
691 len += username.len;
692
693 if (GenerateStuff(this, this->challenge, peer_challenge,
694 username, nt_hash) != SUCCESS)
695 {
696 DBG1(DBG_IKE, "EAP-MS-CHAPv2 generating NT-Response failed");
697 chunk_clear(&nt_hash);
698 return FAILED;
699 }
700 chunk_clear(&nt_hash);
701
702 eap = alloca(len);
703 eap->code = EAP_RESPONSE;
704 eap->identifier = this->identifier;
705 eap->length = htons(len);
706 eap->type = EAP_MSCHAPV2;
707 eap->opcode = MSCHAPV2_RESPONSE;
708 eap->ms_chapv2_id = this->mschapv2id;
709 set_ms_length(eap, len);
710
711 res = (eap_mschapv2_response_t*)eap->data;
712 res->value_size = RESPONSE_LEN;
713 memset(&res->response, 0, RESPONSE_LEN);
714 memcpy(res->response.peer_challenge, peer_challenge.ptr, peer_challenge.len);
715 memcpy(res->response.nt_response, this->nt_response.ptr, this->nt_response.len);
716
717 username = this->peer->get_encoding(this->peer);
718 memcpy(res->name, username.ptr, username.len);
719
720 *out = eap_payload_create_data(chunk_create((void*) eap, len));
721 return NEED_MORE;
722 }
723
724 /**
725 * Process MS-CHAPv2 Success Requests
726 */
727 static status_t process_peer_success(private_eap_mschapv2_t *this,
728 eap_payload_t *in, eap_payload_t **out)
729 {
730 status_t status = FAILED;
731 enumerator_t *enumerator;
732 eap_mschapv2_header_t *eap;
733 chunk_t data, auth_string = chunk_empty;
734 char *message, *token, *msg = NULL;
735 int message_len;
736 u_int16_t len = SHORT_HEADER_LEN;
737
738 data = in->get_data(in);
739 eap = (eap_mschapv2_header_t*)data.ptr;
740
741 if (data.len < AUTH_RESPONSE_LEN)
742 {
743 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
744 return FAILED;
745 }
746
747 message_len = data.len - HEADER_LEN;
748 message = malloc(message_len + 1);
749 memcpy(message, eap->data, message_len);
750 message[message_len] = '\0';
751
752 /* S=<auth_string> M=<msg> */
753 enumerator = enumerator_create_token(message, " ", " ");
754 while (enumerator->enumerate(enumerator, &token))
755 {
756 if (strneq(token, "S=", 2))
757 {
758 chunk_t hex;
759 token += 2;
760 if (strlen(token) != AUTH_RESPONSE_LEN - 2)
761 {
762 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
763 "invalid auth string");
764 goto error;
765 }
766 hex = chunk_create(token, AUTH_RESPONSE_LEN - 2);
767 auth_string = chunk_from_hex(hex, NULL);
768 }
769 else if (strneq(token, "M=", 2))
770 {
771 token += 2;
772 msg = strdup(token);
773 }
774 }
775 enumerator->destroy(enumerator);
776
777 if (auth_string.ptr == NULL)
778 {
779 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
780 "auth string missing");
781 goto error;
782 }
783
784 if (!chunk_equals(this->auth_response, auth_string))
785 {
786 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
787 goto error;
788 }
789
790 DBG1(DBG_IKE, "EAP-MS-CHAPv2 succeeded: '%s'", sanitize(msg));
791
792 eap = alloca(len);
793 eap->code = EAP_RESPONSE;
794 eap->identifier = this->identifier;
795 eap->length = htons(len);
796 eap->type = EAP_MSCHAPV2;
797 eap->opcode = MSCHAPV2_SUCCESS;
798
799 *out = eap_payload_create_data(chunk_create((void*) eap, len));
800 status = NEED_MORE;
801
802 error:
803 chunk_free(&auth_string);
804 free(message);
805 free(msg);
806 return status;
807 }
808
809 static status_t process_peer_failure(private_eap_mschapv2_t *this,
810 eap_payload_t *in, eap_payload_t **out)
811 {
812 status_t status = FAILED;
813 enumerator_t *enumerator;
814 eap_mschapv2_header_t *eap;
815 chunk_t data;
816 char *message, *token, *msg = NULL;
817 int message_len, error = 0, retriable;
818 chunk_t challenge = chunk_empty;
819
820 data = in->get_data(in);
821 eap = (eap_mschapv2_header_t*)data.ptr;
822
823 if (data.len < 3) /* we want at least an error code: E=e */
824 {
825 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
826 return FAILED;
827 }
828
829 message_len = data.len - HEADER_LEN;
830 message = malloc(message_len + 1);
831 memcpy(message, eap->data, message_len);
832 message[message_len] = '\0';
833
834 /* E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=<msg> */
835 enumerator = enumerator_create_token(message, " ", " ");
836 while (enumerator->enumerate(enumerator, &token))
837 {
838 if (strneq(token, "E=", 2))
839 {
840 token += 2;
841 error = atoi(token);
842 }
843 else if (strneq(token, "R=", 2))
844 {
845 token += 2;
846 retriable = atoi(token);
847 }
848 else if (strneq(token, "C=", 2))
849 {
850 chunk_t hex;
851 token += 2;
852 if (strlen(token) != 2 * CHALLENGE_LEN)
853 {
854 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message:"
855 "invalid challenge");
856 goto error;
857 }
858 hex = chunk_create(token, 2 * CHALLENGE_LEN);
859 challenge = chunk_from_hex(hex, NULL);
860 }
861 else if (strneq(token, "V=", 2))
862 {
863 int version;
864 token += 2;
865 version = atoi(token);
866 }
867 else if (strneq(token, "M=", 2))
868 {
869 token += 2;
870 msg = strdup(token);
871 }
872 }
873 enumerator->destroy(enumerator);
874
875 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed with error %N: '%s'",
876 mschapv2_error_names, error, sanitize(msg));
877
878 /**
879 * at this point, if the error is retriable, we MAY retry the authentication
880 * or MAY send a Change Password packet.
881 *
882 * if the error is not retriable (or if we do neither of the above), we
883 * SHOULD send a Failure Response packet.
884 * windows clients don't do that, and since windows server 2008 r2 behaves
885 * pretty odd if we do send a Failure Response, we just don't send one
886 * either. windows 7 actually sends a delete notify (which, according to the
887 * logs, results in an error on windows server 2008 r2).
888 *
889 * btw, windows server 2008 r2 does not send non-retriable errors for e.g.
890 * a disabled account but returns the windows error code in a notify payload
891 * of type 12345.
892 */
893
894 status = FAILED;
895
896 error:
897 chunk_free(&challenge);
898 free(message);
899 free(msg);
900 return status;
901 }
902
903 METHOD(eap_method_t, process_peer, status_t,
904 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
905 {
906 chunk_t data;
907 eap_mschapv2_header_t *eap;
908
909 this->identifier = in->get_identifier(in);
910 data = in->get_data(in);
911 if (data.len < SHORT_HEADER_LEN)
912 {
913 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message");
914 return FAILED;
915 }
916
917 eap = (eap_mschapv2_header_t*)data.ptr;
918
919 switch (eap->opcode)
920 {
921 case MSCHAPV2_CHALLENGE:
922 {
923 return process_peer_challenge(this, in, out);
924 }
925 case MSCHAPV2_SUCCESS:
926 {
927 return process_peer_success(this, in, out);
928 }
929 case MSCHAPV2_FAILURE:
930 {
931 return process_peer_failure(this, in, out);
932 }
933 default:
934 {
935 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
936 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
937 break;
938 }
939 }
940 return FAILED;
941 }
942
943 /**
944 * Handles retries on the server
945 */
946 static status_t process_server_retry(private_eap_mschapv2_t *this,
947 eap_payload_t **out)
948 {
949 eap_mschapv2_header_t *eap;
950 rng_t *rng;
951 chunk_t hex;
952 char msg[FAILURE_MESSAGE_LEN];
953 u_int16_t len = HEADER_LEN + FAILURE_MESSAGE_LEN - 1; /* no null byte */
954
955 if (++this->retries > MAX_RETRIES)
956 {
957 /* we MAY send a Failure Request with R=0, but windows 7 does not
958 * really like that and does not respond with a Failure Response.
959 * so, to clean up our state we just fail with an EAP-Failure.
960 * this gives an unknown error on the windows side, but is also fine
961 * with the standard. */
962 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed: "
963 "maximum number of retries reached");
964 return FAILED;
965 }
966
967 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed, retry (%d)", this->retries);
968
969 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
970 if (!rng)
971 {
972 DBG1(DBG_IKE, "EAP-MS-CHAPv2 failed, no RNG");
973 return FAILED;
974 }
975 rng->get_bytes(rng, CHALLENGE_LEN, this->challenge.ptr);
976 rng->destroy(rng);
977
978 chunk_free(&this->nt_response);
979 chunk_free(&this->auth_response);
980 chunk_free(&this->msk);
981
982 eap = alloca(len);
983 eap->code = EAP_REQUEST;
984 eap->identifier = ++this->identifier;
985 eap->length = htons(len);
986 eap->type = EAP_MSCHAPV2;
987 eap->opcode = MSCHAPV2_FAILURE;
988 eap->ms_chapv2_id = this->mschapv2id++; /* increase for each retry */
989 set_ms_length(eap, len);
990
991 hex = chunk_to_hex(this->challenge, NULL, TRUE);
992 snprintf(msg, FAILURE_MESSAGE_LEN, "%s%s", FAILURE_MESSAGE, hex.ptr);
993 chunk_free(&hex);
994 memcpy(eap->data, msg, FAILURE_MESSAGE_LEN - 1); /* no null byte */
995 *out = eap_payload_create_data(chunk_create((void*) eap, len));
996
997 /* delay the response for some time to make brute-force attacks harder */
998 sleep(RETRY_DELAY);
999
1000 return NEED_MORE;
1001 }
1002
1003 /**
1004 * Process MS-CHAPv2 Response response packets
1005 */
1006 static status_t process_server_response(private_eap_mschapv2_t *this,
1007 eap_payload_t *in, eap_payload_t **out)
1008 {
1009 eap_mschapv2_header_t *eap;
1010 eap_mschapv2_response_t *res;
1011 chunk_t data, peer_challenge, username, nt_hash;
1012 identification_t *userid;
1013 int name_len;
1014 char buf[256];
1015
1016 data = in->get_data(in);
1017 eap = (eap_mschapv2_header_t*)data.ptr;
1018
1019 if (data.len < RESPONSE_PAYLOAD_LEN)
1020 {
1021 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1022 return FAILED;
1023 }
1024
1025 res = (eap_mschapv2_response_t*)eap->data;
1026 peer_challenge = chunk_create(res->response.peer_challenge, CHALLENGE_LEN);
1027
1028 name_len = min(data.len - RESPONSE_PAYLOAD_LEN, 255);
1029 snprintf(buf, sizeof(buf), "%.*s", name_len, res->name);
1030 userid = identification_create_from_string(buf);
1031 DBG2(DBG_IKE, "EAP-MS-CHAPv2 username: '%Y'", userid);
1032 username = extract_username(userid);
1033
1034 if (!get_nt_hash(this, this->server, userid, &nt_hash))
1035 {
1036 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
1037 this->server, userid);
1038 /* FIXME: windows 7 always sends the username that is first entered in
1039 * the username box, even, if the user changes it during retries (probably
1040 * to keep consistent with the EAP-Identity).
1041 * thus, we could actually fail here, because retries do not make much
1042 * sense. on the other hand, an attacker could guess usernames, if the
1043 * error messages were different. */
1044 userid->destroy(userid);
1045 return process_server_retry(this, out);
1046 }
1047
1048 if (GenerateStuff(this, this->challenge, peer_challenge,
1049 username, nt_hash) != SUCCESS)
1050 {
1051 DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
1052 userid->destroy(userid);
1053 chunk_clear(&nt_hash);
1054 return FAILED;
1055 }
1056 userid->destroy(userid);
1057 chunk_clear(&nt_hash);
1058
1059 if (memeq(res->response.nt_response, this->nt_response.ptr,
1060 this->nt_response.len))
1061 {
1062 chunk_t hex;
1063 char msg[AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE)];
1064 u_int16_t len = HEADER_LEN + AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE);
1065
1066 eap = alloca(len);
1067 eap->code = EAP_REQUEST;
1068 eap->identifier = ++this->identifier;
1069 eap->length = htons(len);
1070 eap->type = EAP_MSCHAPV2;
1071 eap->opcode = MSCHAPV2_SUCCESS;
1072 eap->ms_chapv2_id = this->mschapv2id;
1073 set_ms_length(eap, len);
1074
1075 hex = chunk_to_hex(this->auth_response, NULL, TRUE);
1076 snprintf(msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE),
1077 "S=%s%s", hex.ptr, SUCCESS_MESSAGE);
1078 chunk_free(&hex);
1079 memcpy(eap->data, msg, AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE));
1080 *out = eap_payload_create_data(chunk_create((void*) eap, len));
1081 return NEED_MORE;
1082 }
1083
1084 return process_server_retry(this, out);
1085 }
1086
1087 METHOD(eap_method_t, process_server, status_t,
1088 private_eap_mschapv2_t *this, eap_payload_t *in, eap_payload_t **out)
1089 {
1090 eap_mschapv2_header_t *eap;
1091 chunk_t data;
1092
1093 if (this->identifier != in->get_identifier(in))
1094 {
1095 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: "
1096 "unexpected identifier");
1097 return FAILED;
1098 }
1099
1100 data = in->get_data(in);
1101 if (data.len < SHORT_HEADER_LEN)
1102 {
1103 DBG1(DBG_IKE, "received invalid EAP-MS-CHAPv2 message: too short");
1104 return FAILED;
1105 }
1106
1107 eap = (eap_mschapv2_header_t*)data.ptr;
1108
1109 switch (eap->opcode)
1110 {
1111 case MSCHAPV2_RESPONSE:
1112 {
1113 return process_server_response(this, in, out);
1114 }
1115 case MSCHAPV2_SUCCESS:
1116 {
1117 return SUCCESS;
1118 }
1119 case MSCHAPV2_FAILURE:
1120 {
1121 return FAILED;
1122 }
1123 default:
1124 {
1125 DBG1(DBG_IKE, "EAP-MS-CHAPv2 received packet with unsupported "
1126 "OpCode (%N)!", mschapv2_opcode_names, eap->opcode);
1127 break;
1128 }
1129 }
1130 return FAILED;
1131 }
1132
1133 METHOD(eap_method_t, get_type, eap_type_t,
1134 private_eap_mschapv2_t *this, u_int32_t *vendor)
1135 {
1136 *vendor = 0;
1137 return EAP_MSCHAPV2;
1138 }
1139
1140 METHOD(eap_method_t, get_msk, status_t,
1141 private_eap_mschapv2_t *this, chunk_t *msk)
1142 {
1143 if (this->msk.ptr)
1144 {
1145 *msk = this->msk;
1146 return SUCCESS;
1147 }
1148 return FAILED;
1149 }
1150
1151 METHOD(eap_method_t, is_mutual, bool,
1152 private_eap_mschapv2_t *this)
1153 {
1154 return FALSE;
1155 }
1156
1157 METHOD(eap_method_t, destroy, void,
1158 private_eap_mschapv2_t *this)
1159 {
1160 this->peer->destroy(this->peer);
1161 this->server->destroy(this->server);
1162 chunk_free(&this->challenge);
1163 chunk_free(&this->nt_response);
1164 chunk_free(&this->auth_response);
1165 chunk_free(&this->msk);
1166 free(this);
1167 }
1168
1169 /**
1170 * Generic constructor
1171 */
1172 static private_eap_mschapv2_t *eap_mschapv2_create_generic(identification_t *server, identification_t *peer)
1173 {
1174 private_eap_mschapv2_t *this;
1175
1176 INIT(this,
1177 .public = {
1178 .eap_method_interface = {
1179 .get_type = _get_type,
1180 .is_mutual = _is_mutual,
1181 .get_msk = _get_msk,
1182 .destroy = _destroy,
1183 },
1184 },
1185 .peer = peer->clone(peer),
1186 .server = server->clone(server),
1187 );
1188
1189 return this;
1190 }
1191
1192 /*
1193 * see header
1194 */
1195 eap_mschapv2_t *eap_mschapv2_create_server(identification_t *server, identification_t *peer)
1196 {
1197 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1198
1199 this->public.eap_method_interface.initiate = _initiate_server;
1200 this->public.eap_method_interface.process = _process_server;
1201
1202 /* generate a non-zero identifier */
1203 do
1204 {
1205 this->identifier = random();
1206 } while (!this->identifier);
1207
1208 this->mschapv2id = this->identifier;
1209
1210 return &this->public;
1211 }
1212
1213 /*
1214 * see header
1215 */
1216 eap_mschapv2_t *eap_mschapv2_create_peer(identification_t *server, identification_t *peer)
1217 {
1218 private_eap_mschapv2_t *this = eap_mschapv2_create_generic(server, peer);
1219
1220 this->public.eap_method_interface.initiate = _initiate_peer;
1221 this->public.eap_method_interface.process = _process_peer;
1222
1223 return &this->public;
1224 }
1225