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