]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication | |
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 | ||
6226e38d | 15 | #include "utils/includes.h" |
6fc6879b JM |
16 | |
17 | #ifdef CONFIG_RSN_PREAUTH | |
18 | ||
6226e38d JM |
19 | #include "utils/common.h" |
20 | #include "utils/eloop.h" | |
6fc6879b | 21 | #include "l2_packet/l2_packet.h" |
90973fb2 | 22 | #include "common/wpa_common.h" |
281c950b | 23 | #include "eapol_auth/eapol_auth_sm.h" |
e0e14a7b | 24 | #include "eapol_auth/eapol_auth_sm_i.h" |
6226e38d JM |
25 | #include "hostapd.h" |
26 | #include "ap_config.h" | |
27 | #include "ieee802_1x.h" | |
28 | #include "sta_info.h" | |
29 | #include "wpa_auth.h" | |
30 | #include "preauth_auth.h" | |
6fc6879b JM |
31 | |
32 | #ifndef ETH_P_PREAUTH | |
33 | #define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ | |
34 | #endif /* ETH_P_PREAUTH */ | |
35 | ||
36 | static const int dot11RSNAConfigPMKLifetime = 43200; | |
37 | ||
38 | struct rsn_preauth_interface { | |
39 | struct rsn_preauth_interface *next; | |
40 | struct hostapd_data *hapd; | |
41 | struct l2_packet_data *l2; | |
42 | char *ifname; | |
43 | int ifindex; | |
44 | }; | |
45 | ||
46 | ||
47 | static void rsn_preauth_receive(void *ctx, const u8 *src_addr, | |
48 | const u8 *buf, size_t len) | |
49 | { | |
50 | struct rsn_preauth_interface *piface = ctx; | |
51 | struct hostapd_data *hapd = piface->hapd; | |
52 | struct ieee802_1x_hdr *hdr; | |
53 | struct sta_info *sta; | |
54 | struct l2_ethhdr *ethhdr; | |
55 | ||
56 | wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet " | |
57 | "from interface '%s'", piface->ifname); | |
58 | if (len < sizeof(*ethhdr) + sizeof(*hdr)) { | |
59 | wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet " | |
60 | "(len=%lu)", (unsigned long) len); | |
61 | return; | |
62 | } | |
63 | ||
64 | ethhdr = (struct l2_ethhdr *) buf; | |
65 | hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); | |
66 | ||
67 | if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { | |
68 | wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address " | |
69 | MACSTR, MAC2STR(ethhdr->h_dest)); | |
70 | return; | |
71 | } | |
72 | ||
73 | sta = ap_get_sta(hapd, ethhdr->h_source); | |
74 | if (sta && (sta->flags & WLAN_STA_ASSOC)) { | |
75 | wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association " | |
76 | "STA " MACSTR, MAC2STR(sta->addr)); | |
77 | return; | |
78 | } | |
79 | if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { | |
80 | sta = ap_sta_add(hapd, ethhdr->h_source); | |
81 | if (sta == NULL) | |
82 | return; | |
83 | sta->flags = WLAN_STA_PREAUTH; | |
84 | ||
85 | ieee802_1x_new_station(hapd, sta); | |
86 | if (sta->eapol_sm == NULL) { | |
87 | ap_free_sta(hapd, sta); | |
88 | sta = NULL; | |
89 | } else { | |
90 | sta->eapol_sm->radius_identifier = -1; | |
91 | sta->eapol_sm->portValid = TRUE; | |
92 | sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; | |
93 | } | |
94 | } | |
95 | if (sta == NULL) | |
96 | return; | |
97 | sta->preauth_iface = piface; | |
98 | ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), | |
99 | len - sizeof(*ethhdr)); | |
100 | } | |
101 | ||
102 | ||
103 | static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) | |
104 | { | |
105 | struct rsn_preauth_interface *piface; | |
106 | ||
107 | wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname); | |
108 | ||
109 | piface = os_zalloc(sizeof(*piface)); | |
110 | if (piface == NULL) | |
111 | return -1; | |
112 | piface->hapd = hapd; | |
113 | ||
114 | piface->ifname = os_strdup(ifname); | |
115 | if (piface->ifname == NULL) { | |
116 | goto fail1; | |
117 | } | |
118 | ||
119 | piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, | |
120 | rsn_preauth_receive, piface, 1); | |
121 | if (piface->l2 == NULL) { | |
122 | wpa_printf(MSG_ERROR, "Failed to open register layer 2 access " | |
123 | "to ETH_P_PREAUTH"); | |
124 | goto fail2; | |
125 | } | |
126 | ||
127 | piface->next = hapd->preauth_iface; | |
128 | hapd->preauth_iface = piface; | |
129 | return 0; | |
130 | ||
131 | fail2: | |
132 | os_free(piface->ifname); | |
133 | fail1: | |
134 | os_free(piface); | |
135 | return -1; | |
136 | } | |
137 | ||
138 | ||
139 | void rsn_preauth_iface_deinit(struct hostapd_data *hapd) | |
140 | { | |
141 | struct rsn_preauth_interface *piface, *prev; | |
142 | ||
143 | piface = hapd->preauth_iface; | |
144 | hapd->preauth_iface = NULL; | |
145 | while (piface) { | |
146 | prev = piface; | |
147 | piface = piface->next; | |
148 | l2_packet_deinit(prev->l2); | |
149 | os_free(prev->ifname); | |
150 | os_free(prev); | |
151 | } | |
152 | } | |
153 | ||
154 | ||
155 | int rsn_preauth_iface_init(struct hostapd_data *hapd) | |
156 | { | |
157 | char *tmp, *start, *end; | |
158 | ||
159 | if (hapd->conf->rsn_preauth_interfaces == NULL) | |
160 | return 0; | |
161 | ||
162 | tmp = os_strdup(hapd->conf->rsn_preauth_interfaces); | |
163 | if (tmp == NULL) | |
164 | return -1; | |
165 | start = tmp; | |
166 | for (;;) { | |
167 | while (*start == ' ') | |
168 | start++; | |
169 | if (*start == '\0') | |
170 | break; | |
171 | end = os_strchr(start, ' '); | |
172 | if (end) | |
173 | *end = '\0'; | |
174 | ||
175 | if (rsn_preauth_iface_add(hapd, start)) { | |
176 | rsn_preauth_iface_deinit(hapd); | |
1ce77dcc | 177 | os_free(tmp); |
6fc6879b JM |
178 | return -1; |
179 | } | |
180 | ||
181 | if (end) | |
182 | start = end + 1; | |
183 | else | |
184 | break; | |
185 | } | |
186 | os_free(tmp); | |
187 | return 0; | |
188 | } | |
189 | ||
190 | ||
191 | static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) | |
192 | { | |
193 | struct hostapd_data *hapd = eloop_ctx; | |
194 | struct sta_info *sta = timeout_ctx; | |
195 | wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " | |
196 | MACSTR, MAC2STR(sta->addr)); | |
197 | ap_free_sta(hapd, sta); | |
198 | } | |
199 | ||
200 | ||
201 | void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, | |
202 | int success) | |
203 | { | |
204 | const u8 *key; | |
205 | size_t len; | |
206 | hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, | |
207 | HOSTAPD_LEVEL_INFO, "pre-authentication %s", | |
208 | success ? "succeeded" : "failed"); | |
209 | ||
210 | key = ieee802_1x_get_key(sta->eapol_sm, &len); | |
211 | if (len > PMK_LEN) | |
212 | len = PMK_LEN; | |
213 | if (success && key) { | |
214 | if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, | |
215 | sta->addr, | |
216 | dot11RSNAConfigPMKLifetime, | |
217 | sta->eapol_sm) == 0) { | |
218 | hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, | |
219 | HOSTAPD_LEVEL_DEBUG, | |
220 | "added PMKSA cache entry (pre-auth)"); | |
221 | } else { | |
222 | hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, | |
223 | HOSTAPD_LEVEL_DEBUG, | |
224 | "failed to add PMKSA cache entry " | |
225 | "(pre-auth)"); | |
226 | } | |
227 | } | |
228 | ||
229 | /* | |
230 | * Finish STA entry removal from timeout in order to avoid freeing | |
231 | * STA data before the caller has finished processing. | |
232 | */ | |
233 | eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); | |
234 | } | |
235 | ||
236 | ||
237 | void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, | |
238 | u8 *buf, size_t len) | |
239 | { | |
240 | struct rsn_preauth_interface *piface; | |
241 | struct l2_ethhdr *ethhdr; | |
242 | ||
243 | piface = hapd->preauth_iface; | |
244 | while (piface) { | |
245 | if (piface == sta->preauth_iface) | |
246 | break; | |
247 | piface = piface->next; | |
248 | } | |
249 | ||
250 | if (piface == NULL) { | |
251 | wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication " | |
252 | "interface for " MACSTR, MAC2STR(sta->addr)); | |
253 | return; | |
254 | } | |
255 | ||
256 | ethhdr = os_malloc(sizeof(*ethhdr) + len); | |
257 | if (ethhdr == NULL) | |
258 | return; | |
259 | ||
260 | os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); | |
261 | os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); | |
39046253 | 262 | ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH); |
6fc6879b JM |
263 | os_memcpy(ethhdr + 1, buf, len); |
264 | ||
265 | if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, | |
266 | sizeof(*ethhdr) + len) < 0) { | |
267 | wpa_printf(MSG_ERROR, "Failed to send preauth packet using " | |
268 | "l2_packet_send\n"); | |
269 | } | |
270 | os_free(ethhdr); | |
271 | } | |
272 | ||
273 | ||
274 | void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) | |
275 | { | |
276 | eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta); | |
277 | } | |
278 | ||
279 | #endif /* CONFIG_RSN_PREAUTH */ |