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