]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa_supplicant/eap_mschapv2.c
This commit was generated by cvs2svn to compensate for changes in r149749,
[FreeBSD/FreeBSD.git] / contrib / wpa_supplicant / eap_mschapv2.c
1 /*
2  * WPA Supplicant / EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
3  * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18
19 #include "common.h"
20 #include "eap_i.h"
21 #include "wpa_supplicant.h"
22 #include "config_ssid.h"
23 #include "ms_funcs.h"
24
25
26 struct eap_mschapv2_hdr {
27         u8 code;
28         u8 identifier;
29         u16 length; /* including code, identifier, and length */
30         u8 type; /* EAP_TYPE_MSCHAPV2 */
31         u8 op_code; /* MSCHAPV2_OP_* */
32         u8 mschapv2_id; /* usually same as identifier */
33         u8 ms_length[2]; /* Note: misaligned; length - 5 */
34         /* followed by data */
35 } __attribute__ ((packed));
36
37 #define MSCHAPV2_OP_CHALLENGE 1
38 #define MSCHAPV2_OP_RESPONSE 2
39 #define MSCHAPV2_OP_SUCCESS 3
40 #define MSCHAPV2_OP_FAILURE 4
41 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
42
43 #define MSCHAPV2_RESP_LEN 49
44
45 #define ERROR_RESTRICTED_LOGON_HOURS 646
46 #define ERROR_ACCT_DISABLED 647
47 #define ERROR_PASSWD_EXPIRED 648
48 #define ERROR_NO_DIALIN_PERMISSION 649
49 #define ERROR_AUTHENTICATION_FAILURE 691
50 #define ERROR_CHANGING_PASSWORD 709
51
52 #define PASSWD_CHANGE_CHAL_LEN 16
53 #define MSCHAPV2_KEY_LEN 16
54
55
56 struct eap_mschapv2_data {
57         u8 auth_response[20];
58         int auth_response_valid;
59
60         int prev_error;
61         u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
62         int passwd_change_challenge_valid;
63         int passwd_change_version;
64
65         /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
66          */
67         u8 *peer_challenge;
68         u8 *auth_challenge;
69
70         int phase2;
71         u8 master_key[16];
72         int master_key_valid;
73         int success;
74 };
75
76
77 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
78
79
80 static void * eap_mschapv2_init(struct eap_sm *sm)
81 {
82         struct eap_mschapv2_data *data;
83         data = malloc(sizeof(*data));
84         if (data == NULL)
85                 return NULL;
86         memset(data, 0, sizeof(*data));
87
88         if (sm->peer_challenge) {
89                 data->peer_challenge = malloc(16);
90                 if (data->peer_challenge == NULL) {
91                         eap_mschapv2_deinit(sm, data);
92                         return NULL;
93                 }
94                 memcpy(data->peer_challenge, sm->peer_challenge, 16);
95         }
96
97         if (sm->auth_challenge) {
98                 data->auth_challenge = malloc(16);
99                 if (data->auth_challenge == NULL) {
100                         eap_mschapv2_deinit(sm, data);
101                         return NULL;
102                 }
103                 memcpy(data->auth_challenge, sm->auth_challenge, 16);
104         }
105
106         data->phase2 = sm->init_phase2;
107
108         return data;
109 }
110
111
112 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
113 {
114         struct eap_mschapv2_data *data = priv;
115         free(data->peer_challenge);
116         free(data->auth_challenge);
117         free(data);
118 }
119
120
121 static u8 * eap_mschapv2_challenge(struct eap_sm *sm,
122                                    struct eap_mschapv2_data *data,
123                                    struct eap_method_ret *ret,
124                                    struct eap_mschapv2_hdr *req,
125                                    size_t *respDataLen)
126 {
127         struct wpa_ssid *config = eap_get_config(sm);
128         u8 *challenge, *peer_challenge, *username, *pos;
129         int i, ms_len;
130         size_t len, challenge_len, username_len;
131         struct eap_mschapv2_hdr *resp;
132         u8 password_hash[16], password_hash_hash[16];
133
134         /* MSCHAPv2 does not include optional domain name in the
135          * challenge-response calculation, so remove domain prefix
136          * (if present). */
137         username = config->identity;
138         username_len = config->identity_len;
139         for (i = 0; i < username_len; i++) {
140                 if (username[i] == '\\') {
141                         username_len -= i + 1;
142                         username += i + 1;
143                         break;
144                 }
145         }
146
147         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
148         len = be_to_host16(req->length);
149         pos = (u8 *) (req + 1);
150         challenge_len = *pos++;
151         if (challenge_len != 16) {
152                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
153                            "%d", challenge_len);
154                 ret->ignore = TRUE;
155                 return NULL;
156         }
157
158         if (len < 10 || len - 10 < challenge_len) {
159                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
160                            " packet: len=%lu challenge_len=%d",
161                            (unsigned long) len, challenge_len);
162                 ret->ignore = TRUE;
163                 return NULL;
164         }
165
166         challenge = pos;
167         pos += challenge_len;
168         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
169                     pos, len - challenge_len - 10);
170
171         ret->ignore = FALSE;
172         ret->methodState = METHOD_CONT;
173         ret->decision = DECISION_FAIL;
174         ret->allowNotifications = TRUE;
175
176         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
177
178         *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN +
179                 config->identity_len;
180         resp = malloc(*respDataLen);
181         if (resp == NULL)
182                 return NULL;
183         memset(resp, 0, *respDataLen);
184         resp->code = EAP_CODE_RESPONSE;
185         resp->identifier = req->identifier;
186         resp->length = host_to_be16(*respDataLen);
187         resp->type = EAP_TYPE_MSCHAPV2;
188         resp->op_code = MSCHAPV2_OP_RESPONSE;
189         resp->mschapv2_id = req->mschapv2_id;
190         ms_len = *respDataLen - 5;
191         resp->ms_length[0] = ms_len >> 8;
192         resp->ms_length[1] = ms_len & 0xff;
193         pos = (u8 *) (resp + 1);
194         *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */
195
196         /* Response */
197         peer_challenge = pos;
198         if (data->peer_challenge) {
199                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
200                            "in Phase 1");
201                 peer_challenge = data->peer_challenge;
202         } else if (hostapd_get_rand(peer_challenge, 16)) {
203                 free(resp);
204                 return NULL;
205         }
206         pos += 16;
207         pos += 8; /* Reserved, must be zero */
208         if (data->auth_challenge) {
209                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
210                            "in Phase 1");
211                 challenge = data->auth_challenge;
212         }
213         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16);
214         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
215                     peer_challenge, 16);
216         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
217                           username, username_len);
218         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password",
219                               config->password, config->password_len);
220         generate_nt_response(challenge, peer_challenge,
221                              username, username_len,
222                              config->password, config->password_len,
223                              pos);
224         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24);
225         /* Authenticator response is not really needed yet, but calculate it
226          * here so that challenges need not be saved. */
227         generate_authenticator_response(config->password, config->password_len,
228                                         peer_challenge, challenge,
229                                         username, username_len, pos,
230                                         data->auth_response);
231         data->auth_response_valid = 1;
232
233         /* Likewise, generate master_key here since we have the needed data
234          * available. */
235         nt_password_hash(config->password, config->password_len,
236                          password_hash);
237         hash_nt_password_hash(password_hash, password_hash_hash);
238         get_master_key(password_hash_hash, pos /* nt_response */,
239                        data->master_key);
240         data->master_key_valid = 1;
241
242         pos += 24;
243         pos++; /* Flag / reserved, must be zero */
244
245         memcpy(pos, config->identity, config->identity_len);
246         return (u8 *) resp;
247 }
248
249
250 static u8 * eap_mschapv2_success(struct eap_sm *sm,
251                                  struct eap_mschapv2_data *data,
252                                  struct eap_method_ret *ret,
253                                  struct eap_mschapv2_hdr *req,
254                                  size_t *respDataLen)
255 {
256         struct eap_mschapv2_hdr *resp;
257         u8 *pos, recv_response[20];
258         int len, left;
259
260         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
261         len = be_to_host16(req->length);
262         pos = (u8 *) (req + 1);
263         if (!data->auth_response_valid || len < sizeof(*req) + 42 ||
264             pos[0] != 'S' || pos[1] != '=' ||
265             hexstr2bin((char *) (pos + 2), recv_response, 20) ||
266             memcmp(data->auth_response, recv_response, 20) != 0) {
267                 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
268                            "response in success request");
269                 ret->methodState = METHOD_DONE;
270                 ret->decision = DECISION_FAIL;
271                 return NULL;
272         }
273         pos += 42;
274         left = len - sizeof(*req) - 42;
275         while (left > 0 && *pos == ' ') {
276                 pos++;
277                 left--;
278         }
279         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
280                           pos, left);
281         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
282         *respDataLen = 6;
283         resp = malloc(6);
284         if (resp == NULL) {
285                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
286                            "buffer for success response");
287                 ret->ignore = TRUE;
288                 return NULL;
289         }
290
291         resp->code = EAP_CODE_RESPONSE;
292         resp->identifier = req->identifier;
293         resp->length = host_to_be16(6);
294         resp->type = EAP_TYPE_MSCHAPV2;
295         resp->op_code = MSCHAPV2_OP_SUCCESS;
296
297         ret->methodState = METHOD_DONE;
298         ret->decision = DECISION_UNCOND_SUCC;
299         ret->allowNotifications = FALSE;
300         data->success = 1;
301
302         return (u8 *) resp;
303 }
304
305
306 static int eap_mschapv2_failure_txt(struct eap_sm *sm,
307                                     struct eap_mschapv2_data *data, char *txt)
308 {
309         char *pos, *msg = "";
310         int retry = 1;
311         struct wpa_ssid *config = eap_get_config(sm);
312
313         /* For example:
314          * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
315          */
316
317         pos = txt;
318
319         if (pos && strncmp(pos, "E=", 2) == 0) {
320                 pos += 2;
321                 data->prev_error = atoi(pos);
322                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
323                            data->prev_error);
324                 pos = strchr(pos, ' ');
325                 if (pos)
326                         pos++;
327         }
328
329         if (pos && strncmp(pos, "R=", 2) == 0) {
330                 pos += 2;
331                 retry = atoi(pos);
332                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
333                            retry == 1 ? "" : "not ");
334                 pos = strchr(pos, ' ');
335                 if (pos)
336                         pos++;
337         }
338
339         if (pos && strncmp(pos, "C=", 2) == 0) {
340                 int hex_len;
341                 pos += 2;
342                 hex_len = strchr(pos, ' ') - (char *) pos;
343                 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
344                         if (hexstr2bin(pos, data->passwd_change_challenge,
345                                        PASSWD_CHANGE_CHAL_LEN)) {
346                                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
347                                            "failure challenge");
348                         } else {
349                                 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
350                                             "challenge",
351                                             data->passwd_change_challenge,
352                                             PASSWD_CHANGE_CHAL_LEN);
353                                 data->passwd_change_challenge_valid = 1;
354                         }
355                 } else {
356                         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
357                                    "challenge len %d", hex_len);
358                 }
359                 pos = strchr(pos, ' ');
360                 if (pos)
361                         pos++;
362         } else {
363                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
364                            "was not present in failure message");
365         }
366
367         if (pos && strncmp(pos, "V=", 2) == 0) {
368                 pos += 2;
369                 data->passwd_change_version = atoi(pos);
370                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
371                            "protocol version %d", data->passwd_change_version);
372                 pos = strchr(pos, ' ');
373                 if (pos)
374                         pos++;
375         }
376
377         if (pos && strncmp(pos, "M=", 2) == 0) {
378                 pos += 2;
379                 msg = pos;
380         }
381         wpa_msg(sm->msg_ctx, MSG_WARNING,
382                 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
383                 "%d)",
384                 msg, retry == 1 ? "" : "not ", data->prev_error);
385         if (retry == 1 && config) {
386                 /* TODO: could prevent the current password from being used
387                  * again at least for some period of time */
388                 eap_sm_request_identity(sm, config);
389                 eap_sm_request_password(sm, config);
390         } else if (config) {
391                 /* TODO: prevent retries using same username/password */
392         }
393
394         return retry == 1;
395 }
396
397
398 static u8 * eap_mschapv2_failure(struct eap_sm *sm,
399                                  struct eap_mschapv2_data *data,
400                                  struct eap_method_ret *ret,
401                                  struct eap_mschapv2_hdr *req,
402                                  size_t *respDataLen)
403 {
404         struct eap_mschapv2_hdr *resp;
405         u8 *msdata = (u8 *) (req + 1);
406         char *buf;
407         int len = be_to_host16(req->length) - sizeof(*req);
408         int retry = 0;
409
410         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
411         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
412                           msdata, len);
413         buf = malloc(len + 1);
414         if (buf) {
415                 memcpy(buf, msdata, len);
416                 buf[len] = '\0';
417                 retry = eap_mschapv2_failure_txt(sm, data, buf);
418                 free(buf);
419         }
420
421         ret->ignore = FALSE;
422         ret->methodState = METHOD_DONE;
423         ret->decision = DECISION_FAIL;
424         ret->allowNotifications = FALSE;
425
426         if (retry) {
427                 /* TODO: could try to retry authentication, e.g, after having
428                  * changed the username/password. In this case, EAP MS-CHAP-v2
429                  * Failure Response would not be sent here. */
430         }
431
432         *respDataLen = 6;
433         resp = malloc(6);
434         if (resp == NULL) {
435                 return NULL;
436         }
437
438         resp->code = EAP_CODE_RESPONSE;
439         resp->identifier = req->identifier;
440         resp->length = host_to_be16(6);
441         resp->type = EAP_TYPE_MSCHAPV2;
442         resp->op_code = MSCHAPV2_OP_FAILURE;
443
444         return (u8 *) resp;
445 }
446
447
448 static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv,
449                                  struct eap_method_ret *ret,
450                                  u8 *reqData, size_t reqDataLen,
451                                  size_t *respDataLen)
452 {
453         struct eap_mschapv2_data *data = priv;
454         struct wpa_ssid *config = eap_get_config(sm);
455         struct eap_mschapv2_hdr *req;
456         int ms_len, len;
457
458         if (config == NULL || config->identity == NULL) {
459                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
460                 eap_sm_request_identity(sm, config);
461                 ret->ignore = TRUE;
462                 return NULL;
463         }
464
465         if (config->password == NULL) {
466                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
467                 eap_sm_request_password(sm, config);
468                 ret->ignore = TRUE;
469                 return NULL;
470         }
471
472         req = (struct eap_mschapv2_hdr *) reqData;
473         len = be_to_host16(req->length);
474         if (len < sizeof(*req) + 2 || req->type != EAP_TYPE_MSCHAPV2 ||
475             len > reqDataLen) {
476                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
477                 ret->ignore = TRUE;
478                 return NULL;
479         }
480
481         ms_len = ((int) req->ms_length[0] << 8) | req->ms_length[1];
482         if (ms_len != len - 5) {
483                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%d "
484                            "ms_len=%d", len, ms_len);
485                 if (sm->workaround) {
486                         /* Some authentication servers use invalid ms_len,
487                          * ignore it for interoperability. */
488                         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
489                                    " invalid ms_len");
490                 } else {
491                         ret->ignore = TRUE;
492                         return NULL;
493                 }
494         }
495
496         switch (req->op_code) {
497         case MSCHAPV2_OP_CHALLENGE:
498                 return eap_mschapv2_challenge(sm, data, ret, req, respDataLen);
499         case MSCHAPV2_OP_SUCCESS:
500                 return eap_mschapv2_success(sm, data, ret, req, respDataLen);
501         case MSCHAPV2_OP_FAILURE:
502                 return eap_mschapv2_failure(sm, data, ret, req, respDataLen);
503         default:
504                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
505                            req->op_code);
506                 ret->ignore = TRUE;
507                 return NULL;
508         }
509 }
510
511
512 static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
513 {
514         struct eap_mschapv2_data *data = priv;
515         return data->success && data->master_key_valid;
516 }
517
518
519 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
520 {
521         struct eap_mschapv2_data *data = priv;
522         u8 *key;
523         int key_len;
524
525         if (!data->master_key_valid || !data->success)
526                 return NULL;
527
528         if (data->peer_challenge) {
529                 /* EAP-FAST needs both send and receive keys */
530                 key_len = 2 * MSCHAPV2_KEY_LEN;
531         } else {
532                 key_len = MSCHAPV2_KEY_LEN;
533         }
534
535         key = malloc(key_len);
536         if (key == NULL)
537                 return NULL;
538
539         if (data->peer_challenge) {
540                 get_asymetric_start_key(data->master_key, key,
541                                         MSCHAPV2_KEY_LEN, 0, 0);
542                 get_asymetric_start_key(data->master_key,
543                                         key + MSCHAPV2_KEY_LEN,
544                                         MSCHAPV2_KEY_LEN, 1, 0);
545         } else {
546                 get_asymetric_start_key(data->master_key, key,
547                                         MSCHAPV2_KEY_LEN, 1, 0);
548         }
549
550         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
551                         key, key_len);
552
553         *len = key_len;
554         return key;
555 }
556
557
558 const struct eap_method eap_method_mschapv2 =
559 {
560         .method = EAP_TYPE_MSCHAPV2,
561         .name = "MSCHAPV2",
562         .init = eap_mschapv2_init,
563         .deinit = eap_mschapv2_deinit,
564         .process = eap_mschapv2_process,
565         .isKeyAvailable = eap_mschapv2_isKeyAvailable,
566         .getKey = eap_mschapv2_getKey,
567 };