Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / hostapd-0.4.9 / eap_sim.c
1 /*
2  * hostapd / EAP-SIM (draft-haverinen-pppext-eap-sim-15.txt)
3  * Copyright (c) 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 "crypto.h"
23 #include "eap_i.h"
24 #include "eap_sim_common.h"
25 #include "eap_sim_db.h"
26
27
28 #define EAP_SIM_VERSION 1
29
30 /* EAP-SIM Subtypes */
31 #define EAP_SIM_SUBTYPE_START 10
32 #define EAP_SIM_SUBTYPE_CHALLENGE 11
33 #define EAP_SIM_SUBTYPE_NOTIFICATION 12
34 #define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
35 #define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
36
37 /* AT_CLIENT_ERROR_CODE error codes */
38 #define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
39 #define EAP_SIM_UNSUPPORTED_VERSION 1
40 #define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
41 #define EAP_SIM_RAND_NOT_FRESH 3
42
43 #define KC_LEN 8
44 #define SRES_LEN 4
45 #define EAP_SIM_MAX_FAST_REAUTHS 1000
46
47 #define EAP_SIM_MAX_CHAL 3
48
49 struct eap_sim_data {
50         u8 mk[EAP_SIM_MK_LEN];
51         u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
52         u8 k_aut[EAP_SIM_K_AUT_LEN];
53         u8 k_encr[EAP_SIM_K_ENCR_LEN];
54         u8 msk[EAP_SIM_KEYING_DATA_LEN];
55         u8 kc[EAP_SIM_MAX_CHAL][KC_LEN];
56         u8 sres[EAP_SIM_MAX_CHAL][SRES_LEN];
57         u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
58         int num_chal;
59         enum { START, CHALLENGE, SUCCESS, FAILURE } state;
60 };
61
62
63 static const char * eap_sim_state_txt(int state)
64 {
65         switch (state) {
66         case START:
67                 return "START";
68         case CHALLENGE:
69                 return "CHALLENGE";
70         case SUCCESS:
71                 return "SUCCESS";
72         case FAILURE:
73                 return "FAILURE";
74         default:
75                 return "Unknown?!";
76         }
77 }
78
79
80 static void eap_sim_state(struct eap_sim_data *data, int state)
81 {
82         wpa_printf(MSG_DEBUG, "EAP-SIM %s -> %s",
83                    eap_sim_state_txt(data->state),
84                    eap_sim_state_txt(state));
85         data->state = state;
86 }
87
88
89 static void * eap_sim_init(struct eap_sm *sm)
90 {
91         struct eap_sim_data *data;
92
93         if (sm->eap_sim_db_priv == NULL) {
94                 wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
95                 return NULL;
96         }
97
98         data = malloc(sizeof(*data));
99         if (data == NULL)
100                 return data;
101         memset(data, 0, sizeof(*data));
102         data->state = START;
103
104         return data;
105 }
106
107
108 static void eap_sim_reset(struct eap_sm *sm, void *priv)
109 {
110         struct eap_sim_data *data = priv;
111         free(data);
112 }
113
114
115 static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data,
116                                 int id, size_t *reqDataLen)
117 {
118         struct eap_sim_msg *msg;
119         u8 ver[2];
120
121         msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
122                                EAP_SIM_SUBTYPE_START);
123         if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
124                                       sm->identity_len)) {
125                 eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
126         }
127         ver[0] = 0;
128         ver[1] = EAP_SIM_VERSION;
129         eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
130                         ver, sizeof(ver));
131         return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0);
132 }
133
134
135 static u8 * eap_sim_build_challenge(struct eap_sm *sm,
136                                     struct eap_sim_data *data,
137                                     int id, size_t *reqDataLen)
138 {
139         struct eap_sim_msg *msg;
140
141         msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
142                                EAP_SIM_SUBTYPE_CHALLENGE);
143         eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
144                         data->num_chal * GSM_RAND_LEN);
145         eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
146         return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, data->nonce_mt,
147                                   EAP_SIM_NONCE_MT_LEN);
148 }
149
150
151 static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id,
152                              size_t *reqDataLen)
153 {
154         struct eap_sim_data *data = priv;
155
156         switch (data->state) {
157         case START:
158                 return eap_sim_build_start(sm, data, id, reqDataLen);
159         case CHALLENGE:
160                 return eap_sim_build_challenge(sm, data, id, reqDataLen);
161         default:
162                 wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
163                            "buildReq", data->state);
164                 break;
165         }
166         return NULL;
167 }
168
169
170 static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
171                              u8 *respData, size_t respDataLen)
172 {
173         struct eap_sim_data *data = priv;
174         struct eap_hdr *resp;
175         u8 *pos, subtype;
176         size_t len;
177
178         resp = (struct eap_hdr *) respData;
179         pos = (u8 *) (resp + 1);
180         if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM ||
181             (len = ntohs(resp->length)) > respDataLen) {
182                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
183                 return TRUE;
184         }
185         subtype = pos[1];
186
187         if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
188                 return FALSE;
189
190         switch (data->state) {
191         case START:
192                 if (subtype != EAP_SIM_SUBTYPE_START) {
193                         wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
194                                    "subtype %d", subtype);
195                         return TRUE;
196                 }
197                 break;
198         case CHALLENGE:
199                 if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
200                         wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
201                                    "subtype %d", subtype);
202                         return TRUE;
203                 }
204                 break;
205         default:
206                 wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
207                            "processing a response", data->state);
208                 return TRUE;
209         }
210
211         return FALSE;
212 }
213
214
215 static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
216 {
217         return version == EAP_SIM_VERSION;
218 }
219
220
221 static void eap_sim_derive_mk(struct eap_sim_data *data,
222                               const u8 *identity, size_t identity_len,
223                               const u8 *nonce_mt, int selected_version,
224                               int num_chal, const u8 *kc)
225 {
226         u8 sel_ver[2], ver_list[2];
227         const unsigned char *addr[5];
228         size_t len[5];
229
230         addr[0] = identity;
231         addr[1] = kc;
232         addr[2] = nonce_mt;
233         addr[3] = ver_list;
234         addr[4] = sel_ver;
235
236         len[0] = identity_len;
237         len[1] = num_chal * KC_LEN;
238         len[2] = EAP_SIM_NONCE_MT_LEN;
239         len[3] = sizeof(ver_list);
240         len[4] = sizeof(sel_ver);
241
242         ver_list[0] = 0;
243         ver_list[1] = EAP_SIM_VERSION;
244         sel_ver[0] = selected_version >> 8;
245         sel_ver[1] = selected_version & 0xff;
246
247         /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
248         sha1_vector(5, addr, len, data->mk);
249         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN);
250 }
251
252
253 static void eap_sim_process_start(struct eap_sm *sm,
254                                   struct eap_sim_data *data,
255                                   u8 *respData, size_t respDataLen,
256                                   struct eap_sim_attrs *attr)
257 {
258         wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
259
260         if (attr->nonce_mt == NULL || attr->selected_version < 0) {
261                 wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
262                            "required attributes");
263                 eap_sim_state(data, FAILURE);
264                 return;
265         }
266
267         if (!eap_sim_supported_ver(data, attr->selected_version)) {
268                 wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
269                            "version %d", attr->selected_version);
270                 eap_sim_state(data, FAILURE);
271                 return;
272         }
273
274         if (attr->identity) {
275                 free(sm->identity);
276                 sm->identity = malloc(attr->identity_len);
277                 if (sm->identity) {
278                         memcpy(sm->identity, attr->identity,
279                                attr->identity_len);
280                         sm->identity_len = attr->identity_len;
281                 }
282         }
283
284         if (sm->identity == NULL || sm->identity_len < 1 ||
285             sm->identity[0] != '1') {
286                 wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
287                            " user name");
288                 eap_sim_state(data, FAILURE);
289                 return;
290         }
291
292         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
293                           sm->identity, sm->identity_len);
294
295         data->num_chal = eap_sim_db_get_gsm_triplets(
296                 sm->eap_sim_db_priv, sm->identity, sm->identity_len,
297                 EAP_SIM_MAX_CHAL,
298                 (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres);
299         if (data->num_chal < 2) {
300                 wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
301                            "authentication triplets for the peer");
302                 eap_sim_state(data, FAILURE);
303                 return;
304         }
305
306         memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
307         eap_sim_derive_mk(data, sm->identity, sm->identity_len, attr->nonce_mt,
308                           attr->selected_version, data->num_chal,
309                           (u8 *) data->kc);
310         eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk);
311
312         eap_sim_state(data, CHALLENGE);
313 }
314
315
316 static void eap_sim_process_challenge(struct eap_sm *sm,
317                                       struct eap_sim_data *data,
318                                       u8 *respData, size_t respDataLen,
319                                       struct eap_sim_attrs *attr)
320 {
321         if (attr->mac == NULL ||
322             eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac,
323                                (u8 *) data->sres, data->num_chal * SRES_LEN)) {
324                 wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
325                            "did not include valid AT_MAC");
326                 eap_sim_state(data, FAILURE);
327                 return;
328         }
329
330         wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
331                    "correct AT_MAC");
332         eap_sim_state(data, SUCCESS);
333 }
334
335
336 static void eap_sim_process_client_error(struct eap_sm *sm,
337                                          struct eap_sim_data *data,
338                                          u8 *respData, size_t respDataLen,
339                                          struct eap_sim_attrs *attr)
340 {
341         wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
342                    attr->client_error_code);
343         eap_sim_state(data, FAILURE);
344 }
345
346
347 static void eap_sim_process(struct eap_sm *sm, void *priv,
348                             u8 *respData, size_t respDataLen)
349 {
350         struct eap_sim_data *data = priv;
351         struct eap_hdr *resp;
352         u8 *pos, subtype;
353         size_t len;
354         struct eap_sim_attrs attr;
355
356         resp = (struct eap_hdr *) respData;
357         pos = (u8 *) (resp + 1);
358         subtype = pos[1];
359         len = ntohs(resp->length);
360         pos += 4;
361
362         if (eap_sim_parse_attr(pos, respData + len, &attr, 0, 0)) {
363                 wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
364                 eap_sim_state(data, FAILURE);
365                 return;
366         }
367
368         if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
369                 eap_sim_process_client_error(sm, data, respData, len, &attr);
370                 return;
371         }
372
373         switch (data->state) {
374         case START:
375                 eap_sim_process_start(sm, data, respData, len, &attr);
376                 break;
377         case CHALLENGE:
378                 eap_sim_process_challenge(sm, data, respData, len, &attr);
379                 break;
380         default:
381                 wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
382                            "process", data->state);
383                 break;
384         }
385 }
386
387
388 static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
389 {
390         struct eap_sim_data *data = priv;
391         return data->state == SUCCESS || data->state == FAILURE;
392 }
393
394
395 static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
396 {
397         struct eap_sim_data *data = priv;
398         u8 *key;
399
400         if (data->state != SUCCESS)
401                 return NULL;
402
403         key = malloc(EAP_SIM_KEYING_DATA_LEN);
404         if (key == NULL)
405                 return NULL;
406         memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
407         *len = EAP_SIM_KEYING_DATA_LEN;
408         return key;
409 }
410
411
412 static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
413 {
414         struct eap_sim_data *data = priv;
415         return data->state == SUCCESS;
416 }
417
418
419 const struct eap_method eap_method_sim =
420 {
421         .method = EAP_TYPE_SIM,
422         .name = "SIM",
423         .init = eap_sim_init,
424         .reset = eap_sim_reset,
425         .buildReq = eap_sim_buildReq,
426         .check = eap_sim_check,
427         .process = eap_sim_process,
428         .isDone = eap_sim_isDone,
429         .getKey = eap_sim_getKey,
430         .isSuccess = eap_sim_isSuccess,
431 };