Update changes file and submitted to OBS
[framework/connectivity/wpasupplicant.git] / src / eap_server / eap_server_pax.c
1 /*
2  * hostapd / EAP-PAX (RFC 4746) server
3  * Copyright (c) 2005-2007, Jouni Malinen <j@w1.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 "includes.h"
16
17 #include "common.h"
18 #include "crypto/random.h"
19 #include "eap_server/eap_i.h"
20 #include "eap_common/eap_pax_common.h"
21
22 /*
23  * Note: only PAX_STD subprotocol is currently supported
24  *
25  * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
26  * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
27  * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
28  * RSAES-OAEP).
29  */
30
31 struct eap_pax_data {
32         enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
33         u8 mac_id;
34         union {
35                 u8 e[2 * EAP_PAX_RAND_LEN];
36                 struct {
37                         u8 x[EAP_PAX_RAND_LEN]; /* server rand */
38                         u8 y[EAP_PAX_RAND_LEN]; /* client rand */
39                 } r;
40         } rand;
41         u8 ak[EAP_PAX_AK_LEN];
42         u8 mk[EAP_PAX_MK_LEN];
43         u8 ck[EAP_PAX_CK_LEN];
44         u8 ick[EAP_PAX_ICK_LEN];
45         int keys_set;
46         char *cid;
47         size_t cid_len;
48 };
49
50
51 static void * eap_pax_init(struct eap_sm *sm)
52 {
53         struct eap_pax_data *data;
54
55         data = os_zalloc(sizeof(*data));
56         if (data == NULL)
57                 return NULL;
58         data->state = PAX_STD_1;
59         /*
60          * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
61          * supported
62          */
63         data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
64
65         return data;
66 }
67
68
69 static void eap_pax_reset(struct eap_sm *sm, void *priv)
70 {
71         struct eap_pax_data *data = priv;
72         os_free(data->cid);
73         os_free(data);
74 }
75
76
77 static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
78                                            struct eap_pax_data *data, u8 id)
79 {
80         struct wpabuf *req;
81         struct eap_pax_hdr *pax;
82         u8 *pos;
83
84         wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
85
86         if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
87                 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
88                 data->state = FAILURE;
89                 return NULL;
90         }
91
92         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
93                             sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
94                             EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
95         if (req == NULL) {
96                 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
97                            "request");
98                 data->state = FAILURE;
99                 return NULL;
100         }
101
102         pax = wpabuf_put(req, sizeof(*pax));
103         pax->op_code = EAP_PAX_OP_STD_1;
104         pax->flags = 0;
105         pax->mac_id = data->mac_id;
106         pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
107         pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
108
109         wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
110         wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
111         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
112                     data->rand.r.x, EAP_PAX_RAND_LEN);
113
114         pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
115         eap_pax_mac(data->mac_id, (u8 *) "", 0,
116                     wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
117                     NULL, 0, NULL, 0, pos);
118         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
119
120         return req;
121 }
122
123
124 static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
125                                            struct eap_pax_data *data, u8 id)
126 {
127         struct wpabuf *req;
128         struct eap_pax_hdr *pax;
129         u8 *pos;
130
131         wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
132
133         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
134                             sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
135                             EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
136         if (req == NULL) {
137                 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
138                            "request");
139                 data->state = FAILURE;
140                 return NULL;
141         }
142
143         pax = wpabuf_put(req, sizeof(*pax));
144         pax->op_code = EAP_PAX_OP_STD_3;
145         pax->flags = 0;
146         pax->mac_id = data->mac_id;
147         pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
148         pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
149
150         wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
151         pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
152         eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
153                     data->rand.r.y, EAP_PAX_RAND_LEN,
154                     (u8 *) data->cid, data->cid_len, NULL, 0, pos);
155         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
156                     pos, EAP_PAX_MAC_LEN);
157         pos += EAP_PAX_MAC_LEN;
158
159         /* Optional ADE could be added here, if needed */
160
161         pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
162         eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
163                     wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
164                     NULL, 0, NULL, 0, pos);
165         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
166
167         return req;
168 }
169
170
171 static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
172 {
173         struct eap_pax_data *data = priv;
174
175         switch (data->state) {
176         case PAX_STD_1:
177                 return eap_pax_build_std_1(sm, data, id);
178         case PAX_STD_3:
179                 return eap_pax_build_std_3(sm, data, id);
180         default:
181                 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
182                            data->state);
183                 break;
184         }
185         return NULL;
186 }
187
188
189 static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
190                              struct wpabuf *respData)
191 {
192         struct eap_pax_data *data = priv;
193         struct eap_pax_hdr *resp;
194         const u8 *pos;
195         size_t len, mlen;
196         u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
197
198         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
199         if (pos == NULL || len < sizeof(*resp)) {
200                 wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
201                 return TRUE;
202         }
203
204         mlen = sizeof(struct eap_hdr) + 1 + len;
205         resp = (struct eap_pax_hdr *) pos;
206
207         wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
208                    "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
209                    "public_key_id 0x%x",
210                    resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
211                    resp->public_key_id);
212         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
213                     (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
214
215         if (data->state == PAX_STD_1 &&
216             resp->op_code != EAP_PAX_OP_STD_2) {
217                 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
218                            "ignore op %d", resp->op_code);
219                 return TRUE;
220         }
221
222         if (data->state == PAX_STD_3 &&
223             resp->op_code != EAP_PAX_OP_ACK) {
224                 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
225                            "ignore op %d", resp->op_code);
226                 return TRUE;
227         }
228
229         if (resp->op_code != EAP_PAX_OP_STD_2 &&
230             resp->op_code != EAP_PAX_OP_ACK) {
231                 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
232                            resp->op_code);
233         }
234
235         if (data->mac_id != resp->mac_id) {
236                 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
237                            "received 0x%x", data->mac_id, resp->mac_id);
238                 return TRUE;
239         }
240
241         if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
242                 wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
243                            "received 0x%x", EAP_PAX_DH_GROUP_NONE,
244                            resp->dh_group_id);
245                 return TRUE;
246         }
247
248         if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
249                 wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
250                            "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
251                            resp->public_key_id);
252                 return TRUE;
253         }
254
255         if (resp->flags & EAP_PAX_FLAGS_MF) {
256                 /* TODO: add support for reassembling fragments */
257                 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
258                 return TRUE;
259         }
260
261         if (resp->flags & EAP_PAX_FLAGS_CE) {
262                 wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
263                 return TRUE;
264         }
265
266         if (data->keys_set) {
267                 if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
268                         wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
269                         return TRUE;
270                 }
271                 icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
272                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
273                 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
274                             wpabuf_mhead(respData),
275                             wpabuf_len(respData) - EAP_PAX_ICV_LEN,
276                             NULL, 0, NULL, 0, icvbuf);
277                 if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
278                         wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
279                         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
280                                     icvbuf, EAP_PAX_ICV_LEN);
281                         return TRUE;
282                 }
283         }
284
285         return FALSE;
286 }
287
288
289 static void eap_pax_process_std_2(struct eap_sm *sm,
290                                   struct eap_pax_data *data,
291                                   struct wpabuf *respData)
292 {
293         struct eap_pax_hdr *resp;
294         u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
295         const u8 *pos;
296         size_t len, left;
297         int i;
298
299         if (data->state != PAX_STD_1)
300                 return;
301
302         wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
303
304         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
305         if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
306                 return;
307
308         resp = (struct eap_pax_hdr *) pos;
309         pos = (u8 *) (resp + 1);
310         left = len - sizeof(*resp);
311
312         if (left < 2 + EAP_PAX_RAND_LEN ||
313             WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
314                 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
315                 return;
316         }
317         pos += 2;
318         left -= 2;
319         os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
320         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
321                     data->rand.r.y, EAP_PAX_RAND_LEN);
322         pos += EAP_PAX_RAND_LEN;
323         left -= EAP_PAX_RAND_LEN;
324
325         if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
326                 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
327                 return;
328         }
329         data->cid_len = WPA_GET_BE16(pos);
330         os_free(data->cid);
331         data->cid = os_malloc(data->cid_len);
332         if (data->cid == NULL) {
333                 wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
334                            "CID");
335                 return;
336         }
337         os_memcpy(data->cid, pos + 2, data->cid_len);
338         pos += 2 + data->cid_len;
339         left -= 2 + data->cid_len;
340         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
341                           (u8 *) data->cid, data->cid_len);
342
343         if (left < 2 + EAP_PAX_MAC_LEN ||
344             WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
345                 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
346                 return;
347         }
348         pos += 2;
349         left -= 2;
350         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
351                     pos, EAP_PAX_MAC_LEN);
352
353         if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
354                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
355                                   (u8 *) data->cid, data->cid_len);
356                 data->state = FAILURE;
357                 return;
358         }
359
360         for (i = 0;
361              i < EAP_MAX_METHODS &&
362                      (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
363                       sm->user->methods[i].method != EAP_TYPE_NONE);
364              i++) {
365                 if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
366                     sm->user->methods[i].method == EAP_TYPE_PAX)
367                         break;
368         }
369
370         if (i >= EAP_MAX_METHODS ||
371             sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
372             sm->user->methods[i].method != EAP_TYPE_PAX) {
373                 wpa_hexdump_ascii(MSG_DEBUG,
374                                   "EAP-PAX: EAP-PAX not enabled for CID",
375                                   (u8 *) data->cid, data->cid_len);
376                 data->state = FAILURE;
377                 return;
378         }
379
380         if (sm->user->password == NULL ||
381             sm->user->password_len != EAP_PAX_AK_LEN) {
382                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
383                                   "user database for CID",
384                                   (u8 *) data->cid, data->cid_len);
385                 data->state = FAILURE;
386                 return;
387         }
388         os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
389
390         if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
391                                            data->rand.e, data->mk, data->ck,
392                                            data->ick) < 0) {
393                 wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
394                            "key derivation");
395                 data->state = FAILURE;
396                 return;
397         }
398         data->keys_set = 1;
399
400         eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
401                     data->rand.r.x, EAP_PAX_RAND_LEN,
402                     data->rand.r.y, EAP_PAX_RAND_LEN,
403                     (u8 *) data->cid, data->cid_len, mac);
404         if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
405                 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
406                            "PAX_STD-2");
407                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
408                             mac, EAP_PAX_MAC_LEN);
409                 data->state = FAILURE;
410                 return;
411         }
412
413         pos += EAP_PAX_MAC_LEN;
414         left -= EAP_PAX_MAC_LEN;
415
416         if (left < EAP_PAX_ICV_LEN) {
417                 wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
418                            "PAX_STD-2", (unsigned long) left);
419                 return;
420         }
421         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
422         eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
423                     wpabuf_head(respData),
424                     wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
425                     icvbuf);
426         if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
427                 wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
428                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
429                             icvbuf, EAP_PAX_ICV_LEN);
430                 return;
431         }
432         pos += EAP_PAX_ICV_LEN;
433         left -= EAP_PAX_ICV_LEN;
434
435         if (left > 0) {
436                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
437                             pos, left);
438         }
439
440         data->state = PAX_STD_3;
441 }
442
443
444 static void eap_pax_process_ack(struct eap_sm *sm,
445                                 struct eap_pax_data *data,
446                                 struct wpabuf *respData)
447 {
448         if (data->state != PAX_STD_3)
449                 return;
450
451         wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
452                    "completed successfully");
453         data->state = SUCCESS;
454 }
455
456
457 static void eap_pax_process(struct eap_sm *sm, void *priv,
458                             struct wpabuf *respData)
459 {
460         struct eap_pax_data *data = priv;
461         struct eap_pax_hdr *resp;
462         const u8 *pos;
463         size_t len;
464
465         if (sm->user == NULL || sm->user->password == NULL) {
466                 wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
467                            "configured");
468                 data->state = FAILURE;
469                 return;
470         }
471
472         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
473         if (pos == NULL || len < sizeof(*resp))
474                 return;
475
476         resp = (struct eap_pax_hdr *) pos;
477
478         switch (resp->op_code) {
479         case EAP_PAX_OP_STD_2:
480                 eap_pax_process_std_2(sm, data, respData);
481                 break;
482         case EAP_PAX_OP_ACK:
483                 eap_pax_process_ack(sm, data, respData);
484                 break;
485         }
486 }
487
488
489 static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
490 {
491         struct eap_pax_data *data = priv;
492         return data->state == SUCCESS || data->state == FAILURE;
493 }
494
495
496 static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
497 {
498         struct eap_pax_data *data = priv;
499         u8 *key;
500
501         if (data->state != SUCCESS)
502                 return NULL;
503
504         key = os_malloc(EAP_MSK_LEN);
505         if (key == NULL)
506                 return NULL;
507
508         *len = EAP_MSK_LEN;
509         eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
510                     "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
511                     EAP_MSK_LEN, key);
512
513         return key;
514 }
515
516
517 static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
518 {
519         struct eap_pax_data *data = priv;
520         u8 *key;
521
522         if (data->state != SUCCESS)
523                 return NULL;
524
525         key = os_malloc(EAP_EMSK_LEN);
526         if (key == NULL)
527                 return NULL;
528
529         *len = EAP_EMSK_LEN;
530         eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
531                     "Extended Master Session Key",
532                     data->rand.e, 2 * EAP_PAX_RAND_LEN,
533                     EAP_EMSK_LEN, key);
534
535         return key;
536 }
537
538
539 static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
540 {
541         struct eap_pax_data *data = priv;
542         return data->state == SUCCESS;
543 }
544
545
546 int eap_server_pax_register(void)
547 {
548         struct eap_method *eap;
549         int ret;
550
551         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
552                                       EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
553         if (eap == NULL)
554                 return -1;
555
556         eap->init = eap_pax_init;
557         eap->reset = eap_pax_reset;
558         eap->buildReq = eap_pax_buildReq;
559         eap->check = eap_pax_check;
560         eap->process = eap_pax_process;
561         eap->isDone = eap_pax_isDone;
562         eap->getKey = eap_pax_getKey;
563         eap->isSuccess = eap_pax_isSuccess;
564         eap->get_emsk = eap_pax_get_emsk;
565
566         ret = eap_server_method_register(eap);
567         if (ret)
568                 eap_server_method_free(eap);
569         return ret;
570 }