b48e1861c57dbc6b5accebd08a81ce99e18abbf4
[dragonfly.git] / contrib / hostapd / eap_tlv.c
1 /*
2  * hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt)
3  * Copyright (c) 2004-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 "hostapd.h"
18 #include "common.h"
19 #include "eap_i.h"
20
21
22 /* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */
23 #define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
24 #define EAP_TLV_NAK_TLV 4
25 #define EAP_TLV_CRYPTO_BINDING_TLV 5
26 #define EAP_TLV_CONNECTION_BINDING_TLV 6
27 #define EAP_TLV_VENDOR_SPECIFIC_TLV 7
28 #define EAP_TLV_URI_TLV 8
29 #define EAP_TLV_EAP_PAYLOAD_TLV 9
30 #define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
31
32 #define EAP_TLV_RESULT_SUCCESS 1
33 #define EAP_TLV_RESULT_FAILURE 2
34
35
36 struct eap_tlv_data {
37         enum { CONTINUE, SUCCESS, FAILURE } state;
38 };
39
40
41 static void * eap_tlv_init(struct eap_sm *sm)
42 {
43         struct eap_tlv_data *data;
44
45         data = wpa_zalloc(sizeof(*data));
46         if (data == NULL)
47                 return NULL;
48         data->state = CONTINUE;
49
50         return data;
51 }
52
53
54 static void eap_tlv_reset(struct eap_sm *sm, void *priv)
55 {
56         struct eap_tlv_data *data = priv;
57         free(data);
58 }
59
60
61 static u8 * eap_tlv_buildReq(struct eap_sm *sm, void *priv, int id,
62                              size_t *reqDataLen)
63 {
64         struct eap_hdr *req;
65         u8 *pos;
66         u16 status;
67
68         if (sm->tlv_request == TLV_REQ_SUCCESS) {
69                 status = EAP_TLV_RESULT_SUCCESS;
70         } else {
71                 status = EAP_TLV_RESULT_FAILURE;
72         }
73
74         *reqDataLen = sizeof(struct eap_hdr) + 1 + 6;
75         req = malloc(*reqDataLen);
76         if (req == NULL)
77                 return NULL;
78
79         req->code = EAP_CODE_REQUEST;
80         req->identifier = id;
81         req->length = host_to_be16(*reqDataLen);
82         pos = (u8 *) (req + 1);
83         *pos++ = EAP_TYPE_TLV;
84         *pos++ = 0x80; /* Mandatory */
85         *pos++ = EAP_TLV_RESULT_TLV;
86         /* Length */
87         *pos++ = 0;
88         *pos++ = 2;
89         /* Status */
90         *pos++ = status >> 8;
91         *pos++ = status & 0xff;
92
93         return (u8 *) req;
94 }
95
96
97 static Boolean eap_tlv_check(struct eap_sm *sm, void *priv,
98                              u8 *respData, size_t respDataLen)
99 {
100         struct eap_hdr *resp;
101         u8 *pos;
102
103         resp = (struct eap_hdr *) respData;
104         pos = (u8 *) (resp + 1);
105         if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV ||
106             (ntohs(resp->length)) > respDataLen) {
107                 wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame");
108                 return TRUE;
109         }
110
111         return FALSE;
112 }
113
114
115 static void eap_tlv_process(struct eap_sm *sm, void *priv,
116                             u8 *respData, size_t respDataLen)
117 {
118         struct eap_tlv_data *data = priv;
119         struct eap_hdr *resp;
120         u8 *pos;
121         size_t left;
122         u8 *result_tlv = NULL;
123         size_t result_tlv_len = 0;
124         int tlv_type, mandatory, tlv_len;
125
126         resp = (struct eap_hdr *) respData;
127         pos = (u8 *) (resp + 1);
128
129         /* Parse TLVs */
130         left = be_to_host16(resp->length) - sizeof(struct eap_hdr) - 1;
131         pos = (u8 *) (resp + 1);
132         pos++;
133         wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
134         while (left >= 4) {
135                 mandatory = !!(pos[0] & 0x80);
136                 tlv_type = pos[0] & 0x3f;
137                 tlv_type = (tlv_type << 8) | pos[1];
138                 tlv_len = ((int) pos[2] << 8) | pos[3];
139                 pos += 4;
140                 left -= 4;
141                 if ((size_t) tlv_len > left) {
142                         wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
143                                    "(tlv_len=%d left=%lu)", tlv_len,
144                                    (unsigned long) left);
145                         data->state = FAILURE;
146                         return;
147                 }
148                 switch (tlv_type) {
149                 case EAP_TLV_RESULT_TLV:
150                         result_tlv = pos;
151                         result_tlv_len = tlv_len;
152                         break;
153                 default:
154                         wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
155                                    "%d%s", tlv_type,
156                                    mandatory ? " (mandatory)" : "");
157                         if (mandatory) {
158                                 data->state = FAILURE;
159                                 return;
160                         }
161                         /* Ignore this TLV, but process other TLVs */
162                         break;
163                 }
164
165                 pos += tlv_len;
166                 left -= tlv_len;
167         }
168         if (left) {
169                 wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
170                            "Request (left=%lu)", (unsigned long) left);
171                 data->state = FAILURE;
172                 return;
173         }
174
175         /* Process supported TLVs */
176         if (result_tlv) {
177                 int status;
178                 const char *requested;
179
180                 wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
181                             result_tlv, result_tlv_len);
182                 if (result_tlv_len < 2) {
183                         wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
184                                    "(len=%lu)",
185                                    (unsigned long) result_tlv_len);
186                         data->state = FAILURE;
187                         return;
188                 }
189                 requested = sm->tlv_request == TLV_REQ_SUCCESS ? "Success" :
190                         "Failure";
191                 status = ((int) result_tlv[0] << 8) | result_tlv[1];
192                 if (status == EAP_TLV_RESULT_SUCCESS) {
193                         wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
194                                    "- requested %s", requested);
195                         if (sm->tlv_request == TLV_REQ_SUCCESS)
196                                 data->state = SUCCESS;
197                         else
198                                 data->state = FAILURE;
199                         
200                 } else if (status == EAP_TLV_RESULT_FAILURE) {
201                         wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - "
202                                    "requested %s", requested);
203                         if (sm->tlv_request == TLV_REQ_FAILURE)
204                                 data->state = SUCCESS;
205                         else
206                                 data->state = FAILURE;
207                 } else {
208                         wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
209                                    "Status %d", status);
210                         data->state = FAILURE;
211                 }
212         }
213 }
214
215
216 static Boolean eap_tlv_isDone(struct eap_sm *sm, void *priv)
217 {
218         struct eap_tlv_data *data = priv;
219         return data->state != CONTINUE;
220 }
221
222
223 static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv)
224 {
225         struct eap_tlv_data *data = priv;
226         return data->state == SUCCESS;
227 }
228
229
230 int eap_server_tlv_register(void)
231 {
232         struct eap_method *eap;
233         int ret;
234
235         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
236                                       EAP_VENDOR_IETF, EAP_TYPE_TLV, "TLV");
237         if (eap == NULL)
238                 return -1;
239
240         eap->init = eap_tlv_init;
241         eap->reset = eap_tlv_reset;
242         eap->buildReq = eap_tlv_buildReq;
243         eap->check = eap_tlv_check;
244         eap->process = eap_tlv_process;
245         eap->isDone = eap_tlv_isDone;
246         eap->isSuccess = eap_tlv_isSuccess;
247
248         ret = eap_server_method_register(eap);
249         if (ret)
250                 eap_server_method_free(eap);
251         return ret;
252 }