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