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