1 1.1 christos /* 2 1.1 christos * WPA Supplicant - Mesh RSN routines 3 1.1 christos * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. 4 1.1 christos * 5 1.1 christos * This software may be distributed under the terms of the BSD license. 6 1.1 christos * See README for more details. 7 1.1 christos */ 8 1.1 christos 9 1.1 christos #include "utils/includes.h" 10 1.1 christos 11 1.1 christos #include "utils/common.h" 12 1.1 christos #include "utils/eloop.h" 13 1.1 christos #include "crypto/sha256.h" 14 1.1 christos #include "crypto/random.h" 15 1.1 christos #include "crypto/aes.h" 16 1.1 christos #include "crypto/aes_siv.h" 17 1.1 christos #include "rsn_supp/wpa.h" 18 1.1 christos #include "ap/hostapd.h" 19 1.1 christos #include "ap/wpa_auth.h" 20 1.1 christos #include "ap/sta_info.h" 21 1.1 christos #include "ap/ieee802_11.h" 22 1.1 christos #include "wpa_supplicant_i.h" 23 1.1 christos #include "driver_i.h" 24 1.1 christos #include "wpas_glue.h" 25 1.1 christos #include "mesh_mpm.h" 26 1.1 christos #include "mesh_rsn.h" 27 1.1 christos 28 1.1 christos #define MESH_AUTH_TIMEOUT 10 29 1.1 christos #define MESH_AUTH_RETRY 3 30 1.1 christos 31 1.1 christos void mesh_auth_timer(void *eloop_ctx, void *user_data) 32 1.1 christos { 33 1.1 christos struct wpa_supplicant *wpa_s = eloop_ctx; 34 1.1 christos struct sta_info *sta = user_data; 35 1.1.1.2 christos struct hostapd_data *hapd; 36 1.1 christos 37 1.1 christos if (sta->sae->state != SAE_ACCEPTED) { 38 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR 39 1.1 christos " (attempt %d) ", 40 1.1 christos MAC2STR(sta->addr), sta->sae_auth_retry); 41 1.1 christos wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR, 42 1.1 christos MAC2STR(sta->addr)); 43 1.1 christos if (sta->sae_auth_retry < MESH_AUTH_RETRY) { 44 1.1 christos mesh_rsn_auth_sae_sta(wpa_s, sta); 45 1.1 christos } else { 46 1.1.1.2 christos hapd = wpa_s->ifmsh->bss[0]; 47 1.1.1.2 christos 48 1.1 christos if (sta->sae_auth_retry > MESH_AUTH_RETRY) { 49 1.1.1.2 christos ap_free_sta(hapd, sta); 50 1.1 christos return; 51 1.1 christos } 52 1.1 christos 53 1.1 christos /* block the STA if exceeded the number of attempts */ 54 1.1 christos wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED); 55 1.1 christos sta->sae->state = SAE_NOTHING; 56 1.1 christos wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr=" 57 1.1 christos MACSTR " duration=%d", 58 1.1 christos MAC2STR(sta->addr), 59 1.1.1.2 christos hapd->conf->ap_max_inactivity); 60 1.1 christos } 61 1.1 christos sta->sae_auth_retry++; 62 1.1 christos } 63 1.1 christos } 64 1.1 christos 65 1.1 christos 66 1.1 christos static void auth_logger(void *ctx, const u8 *addr, logger_level level, 67 1.1 christos const char *txt) 68 1.1 christos { 69 1.1 christos if (addr) 70 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", 71 1.1 christos MAC2STR(addr), txt); 72 1.1 christos else 73 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: %s", txt); 74 1.1 christos } 75 1.1 christos 76 1.1 christos 77 1.1 christos static const u8 *auth_get_psk(void *ctx, const u8 *addr, 78 1.1.1.3 christos const u8 *p2p_dev_addr, const u8 *prev_psk, 79 1.1.1.4 christos size_t *psk_len, int *vlan_id) 80 1.1 christos { 81 1.1 christos struct mesh_rsn *mesh_rsn = ctx; 82 1.1 christos struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; 83 1.1 christos struct sta_info *sta = ap_get_sta(hapd, addr); 84 1.1 christos 85 1.1.1.3 christos if (psk_len) 86 1.1.1.3 christos *psk_len = PMK_LEN; 87 1.1.1.4 christos if (vlan_id) 88 1.1.1.4 christos *vlan_id = 0; 89 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", 90 1.1 christos __func__, MAC2STR(addr), prev_psk); 91 1.1 christos 92 1.1 christos if (sta && sta->auth_alg == WLAN_AUTH_SAE) { 93 1.1 christos if (!sta->sae || prev_psk) 94 1.1 christos return NULL; 95 1.1 christos return sta->sae->pmk; 96 1.1 christos } 97 1.1 christos 98 1.1 christos return NULL; 99 1.1 christos } 100 1.1 christos 101 1.1 christos 102 1.1 christos static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, 103 1.1.1.5 christos const u8 *addr, int idx, u8 *key, size_t key_len, 104 1.1.1.5 christos enum key_flag key_flag) 105 1.1 christos { 106 1.1 christos struct mesh_rsn *mesh_rsn = ctx; 107 1.1 christos u8 seq[6]; 108 1.1 christos 109 1.1 christos os_memset(seq, 0, sizeof(seq)); 110 1.1 christos 111 1.1 christos if (addr) { 112 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR 113 1.1 christos " key_idx=%d)", 114 1.1 christos __func__, alg, MAC2STR(addr), idx); 115 1.1 christos } else { 116 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)", 117 1.1 christos __func__, alg, idx); 118 1.1 christos } 119 1.1 christos wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); 120 1.1 christos 121 1.1.1.5 christos return wpa_drv_set_key(mesh_rsn->wpa_s, -1, alg, addr, idx, 122 1.1.1.5 christos 1, seq, 6, key, key_len, key_flag); 123 1.1 christos } 124 1.1 christos 125 1.1 christos 126 1.1 christos static int auth_start_ampe(void *ctx, const u8 *addr) 127 1.1 christos { 128 1.1 christos struct mesh_rsn *mesh_rsn = ctx; 129 1.1 christos struct hostapd_data *hapd; 130 1.1 christos struct sta_info *sta; 131 1.1 christos 132 1.1 christos if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH) 133 1.1 christos return -1; 134 1.1 christos 135 1.1 christos hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; 136 1.1 christos sta = ap_get_sta(hapd, addr); 137 1.1 christos if (sta) 138 1.1 christos eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta); 139 1.1 christos 140 1.1 christos mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr); 141 1.1 christos return 0; 142 1.1 christos } 143 1.1 christos 144 1.1 christos 145 1.1.1.5 christos static int auth_for_each_sta( 146 1.1.1.5 christos void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), 147 1.1.1.5 christos void *cb_ctx) 148 1.1.1.5 christos { 149 1.1.1.5 christos struct mesh_rsn *rsn = ctx; 150 1.1.1.5 christos struct hostapd_data *hapd; 151 1.1.1.5 christos struct sta_info *sta; 152 1.1.1.5 christos 153 1.1.1.5 christos hapd = rsn->wpa_s->ifmsh->bss[0]; 154 1.1.1.5 christos for (sta = hapd->sta_list; sta; sta = sta->next) { 155 1.1.1.5 christos if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) 156 1.1.1.5 christos return 1; 157 1.1.1.5 christos } 158 1.1.1.5 christos return 0; 159 1.1.1.5 christos } 160 1.1.1.5 christos 161 1.1.1.5 christos 162 1.1.1.2 christos static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, 163 1.1.1.4 christos enum mfp_options ieee80211w, int ocv) 164 1.1 christos { 165 1.1 christos struct wpa_auth_config conf; 166 1.1.1.3 christos static const struct wpa_auth_callbacks cb = { 167 1.1.1.3 christos .logger = auth_logger, 168 1.1.1.3 christos .get_psk = auth_get_psk, 169 1.1.1.3 christos .set_key = auth_set_key, 170 1.1.1.3 christos .start_ampe = auth_start_ampe, 171 1.1.1.5 christos .for_each_sta = auth_for_each_sta, 172 1.1.1.3 christos }; 173 1.1 christos u8 seq[6] = {}; 174 1.1 christos 175 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); 176 1.1 christos 177 1.1 christos os_memset(&conf, 0, sizeof(conf)); 178 1.1.1.2 christos conf.wpa = WPA_PROTO_RSN; 179 1.1 christos conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE; 180 1.1.1.2 christos conf.wpa_pairwise = rsn->pairwise_cipher; 181 1.1.1.2 christos conf.rsn_pairwise = rsn->pairwise_cipher; 182 1.1.1.2 christos conf.wpa_group = rsn->group_cipher; 183 1.1 christos conf.eapol_version = 0; 184 1.1 christos conf.wpa_group_rekey = -1; 185 1.1.1.3 christos conf.wpa_group_update_count = 4; 186 1.1.1.3 christos conf.wpa_pairwise_update_count = 4; 187 1.1.1.2 christos conf.ieee80211w = ieee80211w; 188 1.1.1.2 christos if (ieee80211w != NO_MGMT_FRAME_PROTECTION) 189 1.1.1.2 christos conf.group_mgmt_cipher = rsn->mgmt_group_cipher; 190 1.1.1.4 christos #ifdef CONFIG_OCV 191 1.1.1.4 christos conf.ocv = ocv; 192 1.1.1.4 christos #endif /* CONFIG_OCV */ 193 1.1 christos 194 1.1.1.3 christos rsn->auth = wpa_init(addr, &conf, &cb, rsn); 195 1.1 christos if (rsn->auth == NULL) { 196 1.1 christos wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); 197 1.1 christos return -1; 198 1.1 christos } 199 1.1 christos 200 1.1 christos /* TODO: support rekeying */ 201 1.1.1.2 christos rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group); 202 1.1.1.2 christos if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0) 203 1.1 christos return -1; 204 1.1.1.2 christos rsn->mgtk_key_id = 1; 205 1.1.1.2 christos 206 1.1.1.2 christos if (ieee80211w != NO_MGMT_FRAME_PROTECTION) { 207 1.1.1.2 christos rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher); 208 1.1.1.2 christos if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0) 209 1.1.1.2 christos return -1; 210 1.1.1.2 christos rsn->igtk_key_id = 4; 211 1.1 christos 212 1.1.1.2 christos /* group mgmt */ 213 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK", 214 1.1.1.2 christos rsn->igtk, rsn->igtk_len); 215 1.1.1.5 christos wpa_drv_set_key(rsn->wpa_s, -1, 216 1.1.1.5 christos wpa_cipher_to_alg(rsn->mgmt_group_cipher), 217 1.1.1.5 christos broadcast_ether_addr, 218 1.1.1.2 christos rsn->igtk_key_id, 1, 219 1.1.1.5 christos seq, sizeof(seq), rsn->igtk, rsn->igtk_len, 220 1.1.1.5 christos KEY_FLAG_GROUP_TX_DEFAULT); 221 1.1.1.2 christos } 222 1.1 christos 223 1.1 christos /* group privacy / data frames */ 224 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK", 225 1.1.1.2 christos rsn->mgtk, rsn->mgtk_len); 226 1.1.1.5 christos wpa_drv_set_key(rsn->wpa_s, -1, wpa_cipher_to_alg(rsn->group_cipher), 227 1.1.1.5 christos broadcast_ether_addr, 228 1.1.1.2 christos rsn->mgtk_key_id, 1, seq, sizeof(seq), 229 1.1.1.5 christos rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT); 230 1.1 christos 231 1.1 christos return 0; 232 1.1 christos } 233 1.1 christos 234 1.1 christos 235 1.1 christos static void mesh_rsn_deinit(struct mesh_rsn *rsn) 236 1.1 christos { 237 1.1 christos os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); 238 1.1.1.2 christos rsn->mgtk_len = 0; 239 1.1.1.2 christos os_memset(rsn->igtk, 0, sizeof(rsn->igtk)); 240 1.1.1.2 christos rsn->igtk_len = 0; 241 1.1.1.2 christos if (rsn->auth) 242 1.1.1.2 christos wpa_deinit(rsn->auth); 243 1.1 christos } 244 1.1 christos 245 1.1 christos 246 1.1 christos struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, 247 1.1 christos struct mesh_conf *conf) 248 1.1 christos { 249 1.1 christos struct mesh_rsn *mesh_rsn; 250 1.1 christos struct hostapd_data *bss = wpa_s->ifmsh->bss[0]; 251 1.1 christos const u8 *ie; 252 1.1 christos size_t ie_len; 253 1.1.1.3 christos #ifdef CONFIG_PMKSA_CACHE_EXTERNAL 254 1.1.1.3 christos struct external_pmksa_cache *entry; 255 1.1.1.3 christos #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ 256 1.1 christos 257 1.1 christos mesh_rsn = os_zalloc(sizeof(*mesh_rsn)); 258 1.1 christos if (mesh_rsn == NULL) 259 1.1 christos return NULL; 260 1.1 christos mesh_rsn->wpa_s = wpa_s; 261 1.1.1.2 christos mesh_rsn->pairwise_cipher = conf->pairwise_cipher; 262 1.1.1.2 christos mesh_rsn->group_cipher = conf->group_cipher; 263 1.1.1.2 christos mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher; 264 1.1 christos 265 1.1.1.2 christos if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr, 266 1.1.1.4 christos conf->ieee80211w, conf->ocv) < 0) { 267 1.1 christos mesh_rsn_deinit(mesh_rsn); 268 1.1.1.2 christos os_free(mesh_rsn); 269 1.1 christos return NULL; 270 1.1 christos } 271 1.1 christos 272 1.1 christos bss->wpa_auth = mesh_rsn->auth; 273 1.1 christos 274 1.1.1.3 christos #ifdef CONFIG_PMKSA_CACHE_EXTERNAL 275 1.1.1.3 christos while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache, 276 1.1.1.3 christos struct external_pmksa_cache, 277 1.1.1.3 christos list)) != NULL) { 278 1.1.1.3 christos int ret; 279 1.1.1.3 christos 280 1.1.1.3 christos ret = wpa_auth_pmksa_add_entry(bss->wpa_auth, 281 1.1.1.3 christos entry->pmksa_cache); 282 1.1.1.3 christos dl_list_del(&entry->list); 283 1.1.1.3 christos os_free(entry); 284 1.1.1.3 christos 285 1.1.1.3 christos if (ret < 0) 286 1.1.1.3 christos return NULL; 287 1.1.1.3 christos } 288 1.1.1.3 christos #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ 289 1.1.1.3 christos 290 1.1 christos ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); 291 1.1.1.2 christos conf->rsn_ie = (u8 *) ie; 292 1.1.1.2 christos conf->rsn_ie_len = ie_len; 293 1.1 christos 294 1.1 christos wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); 295 1.1 christos 296 1.1 christos return mesh_rsn; 297 1.1 christos } 298 1.1 christos 299 1.1 christos 300 1.1 christos static int index_within_array(const int *array, int idx) 301 1.1 christos { 302 1.1 christos int i; 303 1.1 christos 304 1.1 christos for (i = 0; i < idx; i++) { 305 1.1 christos if (array[i] == -1) 306 1.1 christos return 0; 307 1.1 christos } 308 1.1 christos 309 1.1 christos return 1; 310 1.1 christos } 311 1.1 christos 312 1.1 christos 313 1.1 christos static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s, 314 1.1 christos struct sae_data *sae) 315 1.1 christos { 316 1.1 christos int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups; 317 1.1 christos 318 1.1 christos /* Configuration may have changed, so validate current index */ 319 1.1 christos if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index)) 320 1.1 christos return -1; 321 1.1 christos 322 1.1 christos for (;;) { 323 1.1 christos int group = groups[wpa_s->mesh_rsn->sae_group_index]; 324 1.1 christos 325 1.1 christos if (group <= 0) 326 1.1 christos break; 327 1.1 christos if (sae_set_group(sae, group) == 0) { 328 1.1 christos wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", 329 1.1 christos sae->group); 330 1.1 christos return 0; 331 1.1 christos } 332 1.1 christos wpa_s->mesh_rsn->sae_group_index++; 333 1.1 christos } 334 1.1 christos 335 1.1 christos return -1; 336 1.1 christos } 337 1.1 christos 338 1.1 christos 339 1.1 christos static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, 340 1.1 christos struct wpa_ssid *ssid, 341 1.1 christos struct sta_info *sta) 342 1.1 christos { 343 1.1.1.3 christos const char *password; 344 1.1.1.3 christos 345 1.1.1.3 christos password = ssid->sae_password; 346 1.1.1.3 christos if (!password) 347 1.1.1.3 christos password = ssid->passphrase; 348 1.1.1.3 christos if (!password) { 349 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available"); 350 1.1 christos return -1; 351 1.1 christos } 352 1.1 christos 353 1.1 christos if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) { 354 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group"); 355 1.1 christos return -1; 356 1.1 christos } 357 1.1 christos 358 1.1.1.3 christos if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) { 359 1.1.1.3 christos sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id); 360 1.1.1.3 christos if (!sta->sae->tmp->pw_id) 361 1.1.1.3 christos return -1; 362 1.1.1.3 christos } 363 1.1 christos return sae_prepare_commit(wpa_s->own_addr, sta->addr, 364 1.1.1.3 christos (u8 *) password, os_strlen(password), 365 1.1.1.3 christos sta->sae); 366 1.1 christos } 367 1.1 christos 368 1.1 christos 369 1.1 christos /* initiate new SAE authentication with sta */ 370 1.1 christos int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, 371 1.1 christos struct sta_info *sta) 372 1.1 christos { 373 1.1 christos struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; 374 1.1 christos struct wpa_ssid *ssid = wpa_s->current_ssid; 375 1.1.1.2 christos struct rsn_pmksa_cache_entry *pmksa; 376 1.1 christos unsigned int rnd; 377 1.1 christos int ret; 378 1.1 christos 379 1.1 christos if (!ssid) { 380 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, 381 1.1 christos "AUTH: No current_ssid known to initiate new SAE"); 382 1.1 christos return -1; 383 1.1 christos } 384 1.1 christos 385 1.1 christos if (!sta->sae) { 386 1.1 christos sta->sae = os_zalloc(sizeof(*sta->sae)); 387 1.1 christos if (sta->sae == NULL) 388 1.1 christos return -1; 389 1.1 christos } 390 1.1 christos 391 1.1.1.3 christos pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL); 392 1.1.1.2 christos if (pmksa) { 393 1.1.1.2 christos if (!sta->wpa_sm) 394 1.1.1.2 christos sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, 395 1.1.1.2 christos sta->addr, NULL); 396 1.1.1.2 christos if (!sta->wpa_sm) { 397 1.1.1.2 christos wpa_printf(MSG_ERROR, 398 1.1.1.2 christos "mesh: Failed to initialize RSN state machine"); 399 1.1.1.2 christos return -1; 400 1.1.1.2 christos } 401 1.1.1.2 christos 402 1.1.1.2 christos wpa_printf(MSG_DEBUG, 403 1.1.1.2 christos "AUTH: Mesh PMKSA cache entry found for " MACSTR 404 1.1.1.2 christos " - try to use PMKSA caching instead of new SAE authentication", 405 1.1.1.2 christos MAC2STR(sta->addr)); 406 1.1.1.2 christos wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth, 407 1.1.1.5 christos sta->sae->pmkid, sta->sae->pmk, 408 1.1.1.5 christos &sta->sae->pmk_len); 409 1.1.1.2 christos sae_accept_sta(hapd, sta); 410 1.1.1.2 christos sta->mesh_sae_pmksa_caching = 1; 411 1.1.1.2 christos return 0; 412 1.1.1.2 christos } 413 1.1.1.2 christos sta->mesh_sae_pmksa_caching = 0; 414 1.1.1.2 christos 415 1.1 christos if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta)) 416 1.1 christos return -1; 417 1.1 christos 418 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, 419 1.1 christos "AUTH: started authentication with SAE peer: " MACSTR, 420 1.1 christos MAC2STR(sta->addr)); 421 1.1 christos 422 1.1 christos ret = auth_sae_init_committed(hapd, sta); 423 1.1 christos if (ret) 424 1.1 christos return ret; 425 1.1 christos 426 1.1 christos eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta); 427 1.1 christos rnd = rand() % MESH_AUTH_TIMEOUT; 428 1.1 christos eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer, 429 1.1 christos wpa_s, sta); 430 1.1 christos return 0; 431 1.1 christos } 432 1.1 christos 433 1.1 christos 434 1.1 christos void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) 435 1.1 christos { 436 1.1.1.2 christos os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN); 437 1.1 christos } 438 1.1 christos 439 1.1 christos 440 1.1 christos static void 441 1.1 christos mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta) 442 1.1 christos { 443 1.1 christos u8 *myaddr = rsn->wpa_s->own_addr; 444 1.1 christos u8 *peer = sta->addr; 445 1.1.1.2 christos u8 *addr1, *addr2; 446 1.1.1.2 christos u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context; 447 1.1 christos 448 1.1.1.2 christos /* 449 1.1.1.2 christos * AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite || 450 1.1.1.2 christos * min(localMAC, peerMAC) || max(localMAC, peerMAC)) 451 1.1.1.2 christos */ 452 1.1.1.2 christos /* Selected AKM Suite: SAE */ 453 1.1.1.2 christos RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); 454 1.1.1.2 christos ptr += RSN_SELECTOR_LEN; 455 1.1 christos 456 1.1 christos if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { 457 1.1 christos addr1 = myaddr; 458 1.1 christos addr2 = peer; 459 1.1.1.2 christos } else { 460 1.1.1.2 christos addr1 = peer; 461 1.1.1.2 christos addr2 = myaddr; 462 1.1 christos } 463 1.1.1.2 christos os_memcpy(ptr, addr1, ETH_ALEN); 464 1.1.1.2 christos ptr += ETH_ALEN; 465 1.1.1.2 christos os_memcpy(ptr, addr2, ETH_ALEN); 466 1.1 christos 467 1.1 christos sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation", 468 1.1 christos context, sizeof(context), sta->aek, sizeof(sta->aek)); 469 1.1 christos } 470 1.1 christos 471 1.1 christos 472 1.1 christos /* derive mesh temporal key from pmk */ 473 1.1 christos int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) 474 1.1 christos { 475 1.1 christos u8 *ptr; 476 1.1 christos u8 *min, *max; 477 1.1 christos u8 *myaddr = wpa_s->own_addr; 478 1.1 christos u8 *peer = sta->addr; 479 1.1.1.2 christos u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN]; 480 1.1 christos 481 1.1.1.2 christos /* 482 1.1.1.2 christos * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce, 483 1.1.1.2 christos * peerNonce) || max(localNonce, peerNonce) || min(localLinkID, 484 1.1.1.2 christos * peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite || 485 1.1.1.2 christos * min(localMAC, peerMAC) || max(localMAC, peerMAC)) 486 1.1.1.2 christos */ 487 1.1 christos ptr = context; 488 1.1.1.2 christos if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) { 489 1.1 christos min = sta->my_nonce; 490 1.1 christos max = sta->peer_nonce; 491 1.1 christos } else { 492 1.1 christos min = sta->peer_nonce; 493 1.1 christos max = sta->my_nonce; 494 1.1 christos } 495 1.1.1.2 christos os_memcpy(ptr, min, WPA_NONCE_LEN); 496 1.1.1.2 christos ptr += WPA_NONCE_LEN; 497 1.1.1.2 christos os_memcpy(ptr, max, WPA_NONCE_LEN); 498 1.1.1.2 christos ptr += WPA_NONCE_LEN; 499 1.1 christos 500 1.1 christos if (sta->my_lid < sta->peer_lid) { 501 1.1.1.2 christos WPA_PUT_LE16(ptr, sta->my_lid); 502 1.1.1.2 christos ptr += 2; 503 1.1.1.2 christos WPA_PUT_LE16(ptr, sta->peer_lid); 504 1.1.1.2 christos ptr += 2; 505 1.1 christos } else { 506 1.1.1.2 christos WPA_PUT_LE16(ptr, sta->peer_lid); 507 1.1.1.2 christos ptr += 2; 508 1.1.1.2 christos WPA_PUT_LE16(ptr, sta->my_lid); 509 1.1.1.2 christos ptr += 2; 510 1.1 christos } 511 1.1.1.2 christos 512 1.1.1.2 christos /* Selected AKM Suite: SAE */ 513 1.1.1.2 christos RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); 514 1.1.1.2 christos ptr += RSN_SELECTOR_LEN; 515 1.1 christos 516 1.1 christos if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { 517 1.1 christos min = myaddr; 518 1.1 christos max = peer; 519 1.1 christos } else { 520 1.1 christos min = peer; 521 1.1 christos max = myaddr; 522 1.1 christos } 523 1.1 christos os_memcpy(ptr, min, ETH_ALEN); 524 1.1.1.2 christos ptr += ETH_ALEN; 525 1.1.1.2 christos os_memcpy(ptr, max, ETH_ALEN); 526 1.1 christos 527 1.1.1.2 christos sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher); 528 1.1.1.2 christos sha256_prf(sta->sae->pmk, SAE_PMK_LEN, 529 1.1 christos "Temporal Key Derivation", context, sizeof(context), 530 1.1.1.2 christos sta->mtk, sta->mtk_len); 531 1.1 christos return 0; 532 1.1 christos } 533 1.1 christos 534 1.1 christos 535 1.1 christos void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) 536 1.1 christos { 537 1.1.1.2 christos if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) { 538 1.1 christos wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce"); 539 1.1 christos /* TODO: How to handle this more cleanly? */ 540 1.1 christos } 541 1.1.1.2 christos os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN); 542 1.1 christos mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta); 543 1.1 christos } 544 1.1 christos 545 1.1 christos 546 1.1 christos /* insert AMPE and encrypted MIC at @ie. 547 1.1 christos * @mesh_rsn: mesh RSN context 548 1.1 christos * @sta: STA we're sending to 549 1.1 christos * @cat: pointer to category code in frame header. 550 1.1 christos * @buf: wpabuf to add encrypted AMPE and MIC to. 551 1.1 christos * */ 552 1.1 christos int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, 553 1.1 christos const u8 *cat, struct wpabuf *buf) 554 1.1 christos { 555 1.1 christos struct ieee80211_ampe_ie *ampe; 556 1.1 christos u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf); 557 1.1.1.2 christos u8 *ampe_ie, *pos, *mic_payload; 558 1.1 christos const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat }; 559 1.1 christos const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat }; 560 1.1 christos int ret = 0; 561 1.1.1.2 christos size_t len; 562 1.1 christos 563 1.1.1.2 christos len = sizeof(*ampe); 564 1.1.1.2 christos if (cat[1] == PLINK_OPEN) 565 1.1.1.2 christos len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4; 566 1.1.1.2 christos if (cat[1] == PLINK_OPEN && rsn->igtk_len) 567 1.1.1.2 christos len += 2 + 6 + rsn->igtk_len; 568 1.1.1.2 christos 569 1.1.1.2 christos if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) { 570 1.1 christos wpa_printf(MSG_ERROR, "protect frame: buffer too small"); 571 1.1 christos return -EINVAL; 572 1.1 christos } 573 1.1 christos 574 1.1.1.2 christos ampe_ie = os_zalloc(2 + len); 575 1.1 christos if (!ampe_ie) { 576 1.1 christos wpa_printf(MSG_ERROR, "protect frame: out of memory"); 577 1.1 christos return -ENOMEM; 578 1.1 christos } 579 1.1 christos 580 1.1 christos /* IE: AMPE */ 581 1.1 christos ampe_ie[0] = WLAN_EID_AMPE; 582 1.1.1.2 christos ampe_ie[1] = len; 583 1.1 christos ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2); 584 1.1 christos 585 1.1 christos RSN_SELECTOR_PUT(ampe->selected_pairwise_suite, 586 1.1.1.2 christos RSN_CIPHER_SUITE_CCMP); 587 1.1.1.2 christos os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN); 588 1.1.1.2 christos os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN); 589 1.1.1.2 christos 590 1.1.1.2 christos pos = (u8 *) (ampe + 1); 591 1.1.1.2 christos if (cat[1] != PLINK_OPEN) 592 1.1.1.2 christos goto skip_keys; 593 1.1.1.2 christos 594 1.1.1.2 christos /* TODO: Key Replay Counter[8] optionally for 595 1.1.1.2 christos * Mesh Group Key Inform/Acknowledge frames */ 596 1.1.1.2 christos 597 1.1 christos /* TODO: static mgtk for now since we don't support rekeying! */ 598 1.1.1.2 christos /* 599 1.1.1.2 christos * GTKdata[variable]: 600 1.1.1.2 christos * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] 601 1.1.1.2 christos */ 602 1.1.1.2 christos os_memcpy(pos, rsn->mgtk, rsn->mgtk_len); 603 1.1.1.2 christos pos += rsn->mgtk_len; 604 1.1.1.2 christos wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos); 605 1.1.1.2 christos pos += WPA_KEY_RSC_LEN; 606 1.1.1.2 christos /* Use fixed GTKExpirationTime for now */ 607 1.1.1.2 christos WPA_PUT_LE32(pos, 0xffffffff); 608 1.1.1.2 christos pos += 4; 609 1.1.1.2 christos 610 1.1.1.2 christos /* 611 1.1.1.2 christos * IGTKdata[variable]: 612 1.1.1.2 christos * Key ID[2], IPN[6], IGTK[variable] 613 1.1.1.2 christos */ 614 1.1.1.2 christos if (rsn->igtk_len) { 615 1.1.1.2 christos WPA_PUT_LE16(pos, rsn->igtk_key_id); 616 1.1.1.2 christos pos += 2; 617 1.1.1.2 christos wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos); 618 1.1.1.2 christos pos += 6; 619 1.1.1.2 christos os_memcpy(pos, rsn->igtk, rsn->igtk_len); 620 1.1.1.2 christos } 621 1.1.1.2 christos 622 1.1.1.2 christos skip_keys: 623 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element", 624 1.1.1.2 christos ampe_ie, 2 + len); 625 1.1 christos 626 1.1 christos /* IE: MIC */ 627 1.1.1.2 christos wpabuf_put_u8(buf, WLAN_EID_MIC); 628 1.1.1.2 christos wpabuf_put_u8(buf, AES_BLOCK_SIZE); 629 1.1 christos /* MIC field is output ciphertext */ 630 1.1 christos 631 1.1 christos /* encrypt after MIC */ 632 1.1.1.2 christos mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE); 633 1.1 christos 634 1.1.1.3 christos if (aes_siv_encrypt(sta->aek, sizeof(sta->aek), ampe_ie, 2 + len, 3, 635 1.1 christos aad, aad_len, mic_payload)) { 636 1.1 christos wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); 637 1.1 christos ret = -ENOMEM; 638 1.1 christos } 639 1.1 christos 640 1.1 christos os_free(ampe_ie); 641 1.1 christos 642 1.1 christos return ret; 643 1.1 christos } 644 1.1 christos 645 1.1 christos 646 1.1 christos int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, 647 1.1 christos struct ieee802_11_elems *elems, const u8 *cat, 648 1.1.1.2 christos const u8 *chosen_pmk, 649 1.1 christos const u8 *start, size_t elems_len) 650 1.1 christos { 651 1.1 christos int ret = 0; 652 1.1 christos struct ieee80211_ampe_ie *ampe; 653 1.1.1.2 christos u8 null_nonce[WPA_NONCE_LEN] = {}; 654 1.1 christos u8 ampe_eid; 655 1.1 christos u8 ampe_ie_len; 656 1.1.1.2 christos u8 *ampe_buf, *crypt = NULL, *pos, *end; 657 1.1 christos size_t crypt_len; 658 1.1 christos const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; 659 1.1 christos const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, 660 1.1.1.4 christos elems->mic ? (elems->mic - 2) - cat : 0 }; 661 1.1.1.2 christos size_t key_len; 662 1.1.1.2 christos 663 1.1.1.2 christos if (!sta->sae) { 664 1.1.1.2 christos struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; 665 1.1.1.2 christos 666 1.1.1.3 christos if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL)) { 667 1.1.1.2 christos wpa_printf(MSG_INFO, 668 1.1.1.2 christos "Mesh RSN: SAE is not prepared yet"); 669 1.1.1.2 christos return -1; 670 1.1.1.2 christos } 671 1.1.1.2 christos mesh_rsn_auth_sae_sta(wpa_s, sta); 672 1.1.1.2 christos } 673 1.1.1.2 christos 674 1.1.1.4 christos if (chosen_pmk && 675 1.1.1.4 christos (!sta->sae || 676 1.1.1.4 christos os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN) != 0)) { 677 1.1.1.2 christos wpa_msg(wpa_s, MSG_DEBUG, 678 1.1.1.2 christos "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); 679 1.1.1.2 christos return -1; 680 1.1.1.2 christos } 681 1.1 christos 682 1.1 christos if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { 683 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); 684 1.1 christos return -1; 685 1.1 christos } 686 1.1 christos 687 1.1 christos ampe_buf = (u8 *) elems->mic + elems->mic_len; 688 1.1 christos if ((int) elems_len < ampe_buf - start) 689 1.1 christos return -1; 690 1.1 christos 691 1.1 christos crypt_len = elems_len - (elems->mic - start); 692 1.1.1.2 christos if (crypt_len < 2 + AES_BLOCK_SIZE) { 693 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); 694 1.1 christos return -1; 695 1.1 christos } 696 1.1 christos 697 1.1 christos /* crypt is modified by siv_decrypt */ 698 1.1 christos crypt = os_zalloc(crypt_len); 699 1.1 christos if (!crypt) { 700 1.1 christos wpa_printf(MSG_ERROR, "Mesh RSN: out of memory"); 701 1.1 christos ret = -ENOMEM; 702 1.1 christos goto free; 703 1.1 christos } 704 1.1 christos 705 1.1 christos os_memcpy(crypt, elems->mic, crypt_len); 706 1.1 christos 707 1.1.1.3 christos if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3, 708 1.1 christos aad, aad_len, ampe_buf)) { 709 1.1 christos wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); 710 1.1.1.2 christos ret = -2; 711 1.1 christos goto free; 712 1.1 christos } 713 1.1 christos 714 1.1.1.2 christos crypt_len -= AES_BLOCK_SIZE; 715 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element", 716 1.1.1.2 christos ampe_buf, crypt_len); 717 1.1.1.2 christos 718 1.1 christos ampe_eid = *ampe_buf++; 719 1.1 christos ampe_ie_len = *ampe_buf++; 720 1.1 christos 721 1.1 christos if (ampe_eid != WLAN_EID_AMPE || 722 1.1.1.2 christos (size_t) 2 + ampe_ie_len > crypt_len || 723 1.1 christos ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) { 724 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie"); 725 1.1 christos ret = -1; 726 1.1 christos goto free; 727 1.1 christos } 728 1.1 christos 729 1.1 christos ampe = (struct ieee80211_ampe_ie *) ampe_buf; 730 1.1.1.2 christos pos = (u8 *) (ampe + 1); 731 1.1.1.2 christos end = ampe_buf + ampe_ie_len; 732 1.1.1.2 christos if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 && 733 1.1.1.2 christos os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) { 734 1.1 christos wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce"); 735 1.1 christos ret = -1; 736 1.1 christos goto free; 737 1.1 christos } 738 1.1 christos os_memcpy(sta->peer_nonce, ampe->local_nonce, 739 1.1 christos sizeof(ampe->local_nonce)); 740 1.1 christos 741 1.1.1.2 christos /* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge 742 1.1.1.2 christos * frames */ 743 1.1.1.2 christos 744 1.1.1.2 christos /* 745 1.1.1.2 christos * GTKdata shall not be included in Mesh Peering Confirm. While the 746 1.1.1.2 christos * standard does not state the same about IGTKdata, that same constraint 747 1.1.1.2 christos * needs to apply for it. It makes no sense to include the keys in Mesh 748 1.1.1.2 christos * Peering Close frames either, so while the standard does not seem to 749 1.1.1.2 christos * have a shall statement for these, they are described without 750 1.1.1.2 christos * mentioning GTKdata. 751 1.1.1.2 christos * 752 1.1.1.2 christos * An earlier implementation used to add GTKdata to both Mesh Peering 753 1.1.1.2 christos * Open and Mesh Peering Confirm frames, so ignore the possibly present 754 1.1.1.2 christos * GTKdata frame without rejecting the frame as a backwards 755 1.1.1.2 christos * compatibility mechanism. 756 1.1.1.2 christos */ 757 1.1.1.2 christos if (cat[1] != PLINK_OPEN) { 758 1.1.1.2 christos if (end > pos) { 759 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, 760 1.1.1.2 christos "mesh: Ignore unexpected GTKdata(etc.) fields in the end of AMPE element in Mesh Peering Confirm/Close", 761 1.1.1.2 christos pos, end - pos); 762 1.1.1.2 christos } 763 1.1.1.2 christos goto free; 764 1.1.1.2 christos } 765 1.1.1.2 christos 766 1.1.1.2 christos /* 767 1.1.1.2 christos * GTKdata[variable]: 768 1.1.1.2 christos * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] 769 1.1.1.2 christos */ 770 1.1.1.2 christos sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */ 771 1.1.1.2 christos key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher); 772 1.1.1.2 christos if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) { 773 1.1.1.2 christos wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element"); 774 1.1.1.2 christos ret = -1; 775 1.1.1.2 christos goto free; 776 1.1.1.2 christos } 777 1.1.1.2 christos sta->mgtk_len = key_len; 778 1.1.1.2 christos os_memcpy(sta->mgtk, pos, sta->mgtk_len); 779 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK", 780 1.1.1.2 christos sta->mgtk, sta->mgtk_len); 781 1.1.1.2 christos pos += sta->mgtk_len; 782 1.1.1.2 christos wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC", 783 1.1.1.2 christos pos, WPA_KEY_RSC_LEN); 784 1.1.1.2 christos os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc)); 785 1.1.1.2 christos pos += WPA_KEY_RSC_LEN; 786 1.1.1.2 christos wpa_printf(MSG_DEBUG, 787 1.1.1.2 christos "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds", 788 1.1.1.2 christos WPA_GET_LE32(pos)); 789 1.1.1.2 christos pos += 4; 790 1.1.1.2 christos 791 1.1.1.2 christos /* 792 1.1.1.2 christos * IGTKdata[variable]: 793 1.1.1.2 christos * Key ID[2], IPN[6], IGTK[variable] 794 1.1.1.2 christos */ 795 1.1.1.2 christos key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher); 796 1.1.1.2 christos if (end - pos >= (int) (2 + 6 + key_len)) { 797 1.1.1.2 christos sta->igtk_key_id = WPA_GET_LE16(pos); 798 1.1.1.2 christos wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u", 799 1.1.1.2 christos sta->igtk_key_id); 800 1.1.1.2 christos pos += 2; 801 1.1.1.2 christos os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc)); 802 1.1.1.2 christos wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN", 803 1.1.1.2 christos sta->igtk_rsc, sizeof(sta->igtk_rsc)); 804 1.1.1.2 christos pos += 6; 805 1.1.1.2 christos os_memcpy(sta->igtk, pos, key_len); 806 1.1.1.2 christos sta->igtk_len = key_len; 807 1.1.1.2 christos wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK", 808 1.1.1.2 christos sta->igtk, sta->igtk_len); 809 1.1.1.2 christos } 810 1.1.1.2 christos 811 1.1 christos free: 812 1.1 christos os_free(crypt); 813 1.1 christos return ret; 814 1.1 christos } 815