Add -O and -T to the SYNOPSIS.
[dragonfly.git] / contrib / hostapd-0.4.9 / eap_mschapv2.c
1 /*
2  * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
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 #include <netinet/in.h>
19
20 #include "hostapd.h"
21 #include "common.h"
22 #include "eap_i.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; /* must be changed for challenges, but not for
33                          * success/failure */
34         u8 ms_length[2]; /* Note: misaligned; length - 5 */
35         /* followed by data */
36 } __attribute__ ((packed));
37
38 #define MSCHAPV2_OP_CHALLENGE 1
39 #define MSCHAPV2_OP_RESPONSE 2
40 #define MSCHAPV2_OP_SUCCESS 3
41 #define MSCHAPV2_OP_FAILURE 4
42 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
43
44 #define MSCHAPV2_RESP_LEN 49
45
46 #define ERROR_RESTRICTED_LOGON_HOURS 646
47 #define ERROR_ACCT_DISABLED 647
48 #define ERROR_PASSWD_EXPIRED 648
49 #define ERROR_NO_DIALIN_PERMISSION 649
50 #define ERROR_AUTHENTICATION_FAILURE 691
51 #define ERROR_CHANGING_PASSWORD 709
52
53 #define PASSWD_CHANGE_CHAL_LEN 16
54
55
56 #define CHALLENGE_LEN 16
57
58 struct eap_mschapv2_data {
59         u8 auth_challenge[CHALLENGE_LEN];
60         u8 auth_response[20];
61         enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
62         u8 resp_mschapv2_id;
63 };
64
65
66 static void * eap_mschapv2_init(struct eap_sm *sm)
67 {
68         struct eap_mschapv2_data *data;
69
70         data = malloc(sizeof(*data));
71         if (data == NULL)
72                 return data;
73         memset(data, 0, sizeof(*data));
74         data->state = CHALLENGE;
75
76         return data;
77 }
78
79
80 static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
81 {
82         struct eap_mschapv2_data *data = priv;
83         free(data);
84 }
85
86
87 static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm,
88                                          struct eap_mschapv2_data *data,
89                                          int id, size_t *reqDataLen)
90 {
91         struct eap_mschapv2_hdr *req;
92         u8 *pos;
93         char *name = "hostapd"; /* TODO: make this configurable */
94
95         if (hostapd_get_rand(data->auth_challenge, CHALLENGE_LEN)) {
96                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
97                            "data");
98                 data->state = FAILURE;
99                 return NULL;
100         }
101
102         *reqDataLen = sizeof(*req) + 1 + CHALLENGE_LEN + strlen(name);
103         req = malloc(*reqDataLen);
104         if (req == NULL) {
105                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
106                            " for request");
107                 data->state = FAILURE;
108                 return NULL;
109         }
110
111         req->code = EAP_CODE_REQUEST;
112         req->identifier = id;
113         req->length = htons(*reqDataLen);
114         req->type = EAP_TYPE_MSCHAPV2;
115         req->op_code = MSCHAPV2_OP_CHALLENGE;
116         req->mschapv2_id = id;
117         req->ms_length[0] = (*reqDataLen - 5) >> 8;
118         req->ms_length[1] = (*reqDataLen - 5) & 0xff;
119         pos = (u8 *) (req + 1);
120         *pos++ = CHALLENGE_LEN;
121         memcpy(pos, data->auth_challenge, CHALLENGE_LEN);
122         wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", pos,
123                     CHALLENGE_LEN);
124         pos += CHALLENGE_LEN;
125         memcpy(pos, name, strlen(name));
126
127         return (u8 *) req;
128 }
129
130
131 static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm,
132                                            struct eap_mschapv2_data *data,
133                                            int id, size_t *reqDataLen)
134 {
135         struct eap_mschapv2_hdr *req;
136         u8 *pos, *msg, *end;
137         char *message = "OK";
138         size_t msg_len;
139         int i;
140
141         msg_len = 2 + 2 * sizeof(data->auth_response) + 3 + strlen(message);
142         *reqDataLen = sizeof(*req) + msg_len;
143         req = malloc(*reqDataLen + 1);
144         if (req == NULL) {
145                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
146                            " for request");
147                 data->state = FAILURE;
148                 return NULL;
149         }
150
151         req->code = EAP_CODE_REQUEST;
152         req->identifier = id;
153         req->length = htons(*reqDataLen);
154         req->type = EAP_TYPE_MSCHAPV2;
155         req->op_code = MSCHAPV2_OP_SUCCESS;
156         req->mschapv2_id = data->resp_mschapv2_id;
157         req->ms_length[0] = (*reqDataLen - 5) >> 8;
158         req->ms_length[1] = (*reqDataLen - 5) & 0xff;
159
160         msg = pos = (u8 *) (req + 1);
161         end = ((u8 *) req) + *reqDataLen + 1;
162
163         pos += snprintf((char *) pos, end - pos, "S=");
164         for (i = 0; i < sizeof(data->auth_response); i++) {
165                 pos += snprintf((char *) pos, end - pos, "%02X",
166                                 data->auth_response[i]);
167         }
168         pos += snprintf((char *) pos, end - pos, " M=%s", message);
169
170         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
171                           msg, msg_len);
172
173         return (u8 *) req;
174 }
175
176
177 static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm,
178                                            struct eap_mschapv2_data *data,
179                                            int id, size_t *reqDataLen)
180 {
181         struct eap_mschapv2_hdr *req;
182         u8 *pos;
183         char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
184                 "M=FAILED";
185         size_t msg_len;
186
187         msg_len = strlen(message);
188         *reqDataLen = sizeof(*req) + msg_len;
189         req = malloc(*reqDataLen + 1);
190         if (req == NULL) {
191                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
192                            " for request");
193                 data->state = FAILURE;
194                 return NULL;
195         }
196
197         req->code = EAP_CODE_REQUEST;
198         req->identifier = id;
199         req->length = htons(*reqDataLen);
200         req->type = EAP_TYPE_MSCHAPV2;
201         req->op_code = MSCHAPV2_OP_FAILURE;
202         req->mschapv2_id = data->resp_mschapv2_id;
203         req->ms_length[0] = (*reqDataLen - 5) >> 8;
204         req->ms_length[1] = (*reqDataLen - 5) & 0xff;
205
206         pos = (u8 *) (req + 1);
207         memcpy(pos, message, msg_len);
208
209         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
210                           (u8 *) message, msg_len);
211
212         return (u8 *) req;
213 }
214
215
216 static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id,
217                                   size_t *reqDataLen)
218 {
219         struct eap_mschapv2_data *data = priv;
220
221         switch (data->state) {
222         case CHALLENGE:
223                 return eap_mschapv2_build_challenge(sm, data, id, reqDataLen);
224         case SUCCESS_REQ:
225                 return eap_mschapv2_build_success_req(sm, data, id,
226                                                       reqDataLen);
227         case FAILURE_REQ:
228                 return eap_mschapv2_build_failure_req(sm, data, id,
229                                                       reqDataLen);
230         default:
231                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
232                            "buildReq", data->state);
233                 break;
234         }
235         return NULL;
236 }
237
238
239 static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
240                                   u8 *respData, size_t respDataLen)
241 {
242         struct eap_mschapv2_data *data = priv;
243         struct eap_mschapv2_hdr *resp;
244         u8 *pos;
245         size_t len;
246
247         resp = (struct eap_mschapv2_hdr *) respData;
248         pos = (u8 *) (resp + 1);
249         if (respDataLen < 6 || resp->type != EAP_TYPE_MSCHAPV2 ||
250             (len = ntohs(resp->length)) > respDataLen) {
251                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
252                 return TRUE;
253         }
254
255         if (data->state == CHALLENGE &&
256             resp->op_code != MSCHAPV2_OP_RESPONSE) {
257                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
258                            "ignore op %d", resp->op_code);
259                 return TRUE;
260         }
261
262         if (data->state == SUCCESS_REQ &&
263             resp->op_code != MSCHAPV2_OP_SUCCESS &&
264             resp->op_code != MSCHAPV2_OP_FAILURE) {
265                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
266                            "Failure - ignore op %d", resp->op_code);
267                 return TRUE;
268         }
269
270         if (data->state == FAILURE_REQ &&
271             resp->op_code != MSCHAPV2_OP_FAILURE) {
272                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
273                            "- ignore op %d", resp->op_code);
274                 return TRUE;
275         }
276
277         return FALSE;
278 }
279
280
281 static void eap_mschapv2_process_response(struct eap_sm *sm,
282                                           struct eap_mschapv2_data *data,
283                                           u8 *respData, size_t respDataLen)
284 {
285         struct eap_mschapv2_hdr *resp;
286         u8 *pos;
287         u8 *peer_challenge, *nt_response, flags, *name;
288         size_t name_len;
289         u8 expected[24];
290         int i;
291         u8 *username, *user;
292         size_t username_len, user_len;
293
294         resp = (struct eap_mschapv2_hdr *) respData;
295         pos = (u8 *) (resp + 1);
296
297         if (respDataLen < sizeof(*resp) + 1 + 49 ||
298             resp->op_code != MSCHAPV2_OP_RESPONSE ||
299             pos[0] != 49) {
300                 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
301                             respData, respDataLen);
302                 data->state = FAILURE;
303                 return;
304         }
305         data->resp_mschapv2_id = resp->mschapv2_id;
306         pos++;
307         peer_challenge = pos;
308         pos += 16 + 8;
309         nt_response = pos;
310         pos += 24;
311         flags = *pos++;
312         name = pos;
313         name_len = respData + respDataLen - name;
314
315         wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
316                     peer_challenge, 16);
317         wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
318         wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
319         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
320
321         /* MSCHAPv2 does not include optional domain name in the
322          * challenge-response calculation, so remove domain prefix
323          * (if present). */
324         username = sm->identity;
325         username_len = sm->identity_len;
326         for (i = 0; i < username_len; i++) {
327                 if (username[i] == '\\') {
328                         username_len -= i + 1;
329                         username += i + 1;
330                         break;
331                 }
332         }
333
334         user = name;
335         user_len = name_len;
336         for (i = 0; i < user_len; i++) {
337                 if (user[i] == '\\') {
338                         user_len -= i + 1;
339                         user += i + 1;
340                         break;
341                 }
342         }
343
344         if (username_len != user_len ||
345             memcmp(username, user, username_len) != 0) {
346                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
347                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
348                                   "name", username, username_len);
349                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
350                                   "name", user, user_len);
351                 data->state = FAILURE;
352                 return;
353         }
354
355         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
356                           username, username_len);
357
358         generate_nt_response(data->auth_challenge, peer_challenge,
359                              username, username_len,
360                              sm->user->password, sm->user->password_len,
361                              expected);
362
363         if (memcmp(nt_response, expected, 24) == 0) {
364                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
365                 data->state = SUCCESS_REQ;
366
367
368                 /* Authenticator response is not really needed yet, but
369                  * calculate it here so that peer_challenge and username need
370                  * not be saved. */
371                 generate_authenticator_response(sm->user->password,
372                                                 sm->user->password_len,
373                                                 peer_challenge,
374                                                 data->auth_challenge,
375                                                 username, username_len,
376                                                 nt_response,
377                                                 data->auth_response);
378         } else {
379                 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
380                             expected, 24);
381                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
382                 data->state = FAILURE_REQ;
383         }
384 }
385
386
387 static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
388                                               struct eap_mschapv2_data *data,
389                                               u8 *respData, size_t respDataLen)
390 {
391         struct eap_mschapv2_hdr *resp;
392
393         resp = (struct eap_mschapv2_hdr *) respData;
394
395         if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
396                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
397                            " - authentication completed successfully");
398                 data->state = SUCCESS;
399         } else {
400                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
401                            "Response - peer rejected authentication");
402                 data->state = FAILURE;
403         }
404 }
405
406
407 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
408                                               struct eap_mschapv2_data *data,
409                                               u8 *respData, size_t respDataLen)
410 {
411         struct eap_mschapv2_hdr *resp;
412
413         resp = (struct eap_mschapv2_hdr *) respData;
414
415         if (resp->op_code == MSCHAPV2_OP_FAILURE) {
416                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
417                            " - authentication failed");
418         } else {
419                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
420                            "Response - authentication failed");
421         }
422
423         data->state = FAILURE;
424 }
425
426
427 static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
428                                  u8 *respData, size_t respDataLen)
429 {
430         struct eap_mschapv2_data *data = priv;
431
432         if (sm->user == NULL || sm->user->password == NULL) {
433                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
434                 data->state = FAILURE;
435                 return;
436         }
437
438         switch (data->state) {
439         case CHALLENGE:
440                 eap_mschapv2_process_response(sm, data, respData, respDataLen);
441                 break;
442         case SUCCESS_REQ:
443                 eap_mschapv2_process_success_resp(sm, data, respData,
444                                                   respDataLen);
445                 break;
446         case FAILURE_REQ:
447                 eap_mschapv2_process_failure_resp(sm, data, respData,
448                                                   respDataLen);
449                 break;
450         default:
451                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
452                            "process", data->state);
453                 break;
454         }
455 }
456
457
458 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
459 {
460         struct eap_mschapv2_data *data = priv;
461         return data->state == SUCCESS || data->state == FAILURE;
462 }
463
464
465 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
466 {
467         struct eap_mschapv2_data *data = priv;
468         return data->state == SUCCESS;
469 }
470
471
472 const struct eap_method eap_method_mschapv2 =
473 {
474         .method = EAP_TYPE_MSCHAPV2,
475         .name = "MSCHAPV2",
476         .init = eap_mschapv2_init,
477         .reset = eap_mschapv2_reset,
478         .buildReq = eap_mschapv2_buildReq,
479         .check = eap_mschapv2_check,
480         .process = eap_mschapv2_process,
481         .isDone = eap_mschapv2_isDone,
482         .isSuccess = eap_mschapv2_isSuccess,
483 };