ieee802_11_auth.c revision b97e428f8acf1ecb93f38f8d0063d2f2fd0bc36e
1/* 2 * hostapd / IEEE 802.11 authentication (ACL) 3 * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 * 8 * Access control list for IEEE 802.11 authentication can uses statically 9 * configured ACL from configuration files or an external RADIUS server. 10 * Results from external RADIUS queries are cached to allow faster 11 * authentication frame processing. 12 */ 13 14#include "utils/includes.h" 15 16#include "utils/common.h" 17#include "utils/eloop.h" 18#include "crypto/sha1.h" 19#include "radius/radius.h" 20#include "radius/radius_client.h" 21#include "hostapd.h" 22#include "ap_config.h" 23#include "ap_drv_ops.h" 24#include "ieee802_11.h" 25#include "ieee802_1x.h" 26#include "ieee802_11_auth.h" 27 28#define RADIUS_ACL_TIMEOUT 30 29 30 31struct hostapd_cached_radius_acl { 32 struct os_reltime timestamp; 33 macaddr addr; 34 int accepted; /* HOSTAPD_ACL_* */ 35 struct hostapd_cached_radius_acl *next; 36 u32 session_timeout; 37 u32 acct_interim_interval; 38 int vlan_id; 39 struct hostapd_sta_wpa_psk_short *psk; 40 char *identity; 41 char *radius_cui; 42}; 43 44 45struct hostapd_acl_query_data { 46 struct os_reltime timestamp; 47 u8 radius_id; 48 macaddr addr; 49 u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ 50 size_t auth_msg_len; 51 struct hostapd_acl_query_data *next; 52}; 53 54 55#ifndef CONFIG_NO_RADIUS 56static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) 57{ 58 os_free(e->identity); 59 os_free(e->radius_cui); 60 hostapd_free_psk_list(e->psk); 61 os_free(e); 62} 63 64 65static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) 66{ 67 struct hostapd_cached_radius_acl *prev; 68 69 while (acl_cache) { 70 prev = acl_cache; 71 acl_cache = acl_cache->next; 72 hostapd_acl_cache_free_entry(prev); 73 } 74} 75 76 77static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, 78 struct hostapd_sta_wpa_psk_short *src) 79{ 80 struct hostapd_sta_wpa_psk_short **copy_to; 81 struct hostapd_sta_wpa_psk_short *copy_from; 82 83 /* Copy PSK linked list */ 84 copy_to = psk; 85 copy_from = src; 86 while (copy_from && copy_to) { 87 *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); 88 if (*copy_to == NULL) 89 break; 90 os_memcpy(*copy_to, copy_from, 91 sizeof(struct hostapd_sta_wpa_psk_short)); 92 copy_from = copy_from->next; 93 copy_to = &((*copy_to)->next); 94 } 95 if (copy_to) 96 *copy_to = NULL; 97} 98 99 100static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, 101 u32 *session_timeout, 102 u32 *acct_interim_interval, int *vlan_id, 103 struct hostapd_sta_wpa_psk_short **psk, 104 char **identity, char **radius_cui) 105{ 106 struct hostapd_cached_radius_acl *entry; 107 struct os_reltime now; 108 109 os_get_reltime(&now); 110 111 for (entry = hapd->acl_cache; entry; entry = entry->next) { 112 if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) 113 continue; 114 115 if (os_reltime_expired(&now, &entry->timestamp, 116 RADIUS_ACL_TIMEOUT)) 117 return -1; /* entry has expired */ 118 if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) 119 if (session_timeout) 120 *session_timeout = entry->session_timeout; 121 if (acct_interim_interval) 122 *acct_interim_interval = 123 entry->acct_interim_interval; 124 if (vlan_id) 125 *vlan_id = entry->vlan_id; 126 copy_psk_list(psk, entry->psk); 127 if (identity) { 128 if (entry->identity) 129 *identity = os_strdup(entry->identity); 130 else 131 *identity = NULL; 132 } 133 if (radius_cui) { 134 if (entry->radius_cui) 135 *radius_cui = os_strdup(entry->radius_cui); 136 else 137 *radius_cui = NULL; 138 } 139 return entry->accepted; 140 } 141 142 return -1; 143} 144#endif /* CONFIG_NO_RADIUS */ 145 146 147static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) 148{ 149 if (query == NULL) 150 return; 151 os_free(query->auth_msg); 152 os_free(query); 153} 154 155 156#ifndef CONFIG_NO_RADIUS 157static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, 158 struct hostapd_acl_query_data *query) 159{ 160 struct radius_msg *msg; 161 char buf[128]; 162 163 query->radius_id = radius_client_get_id(hapd->radius); 164 msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id); 165 if (msg == NULL) 166 return -1; 167 168 if (radius_msg_make_authenticator(msg) < 0) { 169 wpa_printf(MSG_INFO, "Could not make Request Authenticator"); 170 goto fail; 171 } 172 173 os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); 174 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, 175 os_strlen(buf))) { 176 wpa_printf(MSG_DEBUG, "Could not add User-Name"); 177 goto fail; 178 } 179 180 if (!radius_msg_add_attr_user_password( 181 msg, (u8 *) buf, os_strlen(buf), 182 hapd->conf->radius->auth_server->shared_secret, 183 hapd->conf->radius->auth_server->shared_secret_len)) { 184 wpa_printf(MSG_DEBUG, "Could not add User-Password"); 185 goto fail; 186 } 187 188 if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, 189 NULL, msg) < 0) 190 goto fail; 191 192 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, 193 MAC2STR(addr)); 194 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, 195 (u8 *) buf, os_strlen(buf))) { 196 wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id"); 197 goto fail; 198 } 199 200 os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); 201 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, 202 (u8 *) buf, os_strlen(buf))) { 203 wpa_printf(MSG_DEBUG, "Could not add Connect-Info"); 204 goto fail; 205 } 206 207 if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) 208 goto fail; 209 return 0; 210 211 fail: 212 radius_msg_free(msg); 213 return -1; 214} 215#endif /* CONFIG_NO_RADIUS */ 216 217 218/** 219 * hostapd_check_acl - Check a specified STA against accept/deny ACLs 220 * @hapd: hostapd BSS data 221 * @addr: MAC address of the STA 222 * @vlan_id: Buffer for returning VLAN ID 223 * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING 224 */ 225 int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id) 226{ 227 if (hostapd_maclist_found(hapd->conf->accept_mac, 228 hapd->conf->num_accept_mac, addr, vlan_id)) 229 return HOSTAPD_ACL_ACCEPT; 230 231 if (hostapd_maclist_found(hapd->conf->deny_mac, 232 hapd->conf->num_deny_mac, addr, vlan_id)) 233 return HOSTAPD_ACL_REJECT; 234 235 if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) 236 return HOSTAPD_ACL_ACCEPT; 237 if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) 238 return HOSTAPD_ACL_REJECT; 239 240 return HOSTAPD_ACL_PENDING; 241} 242 243 244/** 245 * hostapd_allowed_address - Check whether a specified STA can be authenticated 246 * @hapd: hostapd BSS data 247 * @addr: MAC address of the STA 248 * @msg: Authentication message 249 * @len: Length of msg in octets 250 * @session_timeout: Buffer for returning session timeout (from RADIUS) 251 * @acct_interim_interval: Buffer for returning account interval (from RADIUS) 252 * @vlan_id: Buffer for returning VLAN ID 253 * @psk: Linked list buffer for returning WPA PSK 254 * @identity: Buffer for returning identity (from RADIUS) 255 * @radius_cui: Buffer for returning CUI (from RADIUS) 256 * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING 257 * 258 * The caller is responsible for freeing the returned *identity and *radius_cui 259 * values with os_free(). 260 */ 261int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, 262 const u8 *msg, size_t len, u32 *session_timeout, 263 u32 *acct_interim_interval, int *vlan_id, 264 struct hostapd_sta_wpa_psk_short **psk, 265 char **identity, char **radius_cui) 266{ 267 int res; 268 269 if (session_timeout) 270 *session_timeout = 0; 271 if (acct_interim_interval) 272 *acct_interim_interval = 0; 273 if (vlan_id) 274 *vlan_id = 0; 275 if (psk) 276 *psk = NULL; 277 if (identity) 278 *identity = NULL; 279 if (radius_cui) 280 *radius_cui = NULL; 281 282 res = hostapd_check_acl(hapd, addr, vlan_id); 283 if (res != HOSTAPD_ACL_PENDING) 284 return res; 285 286 if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { 287#ifdef CONFIG_NO_RADIUS 288 return HOSTAPD_ACL_REJECT; 289#else /* CONFIG_NO_RADIUS */ 290 struct hostapd_acl_query_data *query; 291 292 /* Check whether ACL cache has an entry for this station */ 293 res = hostapd_acl_cache_get(hapd, addr, session_timeout, 294 acct_interim_interval, vlan_id, psk, 295 identity, radius_cui); 296 if (res == HOSTAPD_ACL_ACCEPT || 297 res == HOSTAPD_ACL_ACCEPT_TIMEOUT) 298 return res; 299 if (res == HOSTAPD_ACL_REJECT) 300 return HOSTAPD_ACL_REJECT; 301 302 query = hapd->acl_queries; 303 while (query) { 304 if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { 305 /* pending query in RADIUS retransmit queue; 306 * do not generate a new one */ 307 if (identity) { 308 os_free(*identity); 309 *identity = NULL; 310 } 311 if (radius_cui) { 312 os_free(*radius_cui); 313 *radius_cui = NULL; 314 } 315 return HOSTAPD_ACL_PENDING; 316 } 317 query = query->next; 318 } 319 320 if (!hapd->conf->radius->auth_server) 321 return HOSTAPD_ACL_REJECT; 322 323 /* No entry in the cache - query external RADIUS server */ 324 query = os_zalloc(sizeof(*query)); 325 if (query == NULL) { 326 wpa_printf(MSG_ERROR, "malloc for query data failed"); 327 return HOSTAPD_ACL_REJECT; 328 } 329 os_get_reltime(&query->timestamp); 330 os_memcpy(query->addr, addr, ETH_ALEN); 331 if (hostapd_radius_acl_query(hapd, addr, query)) { 332 wpa_printf(MSG_DEBUG, "Failed to send Access-Request " 333 "for ACL query."); 334 hostapd_acl_query_free(query); 335 return HOSTAPD_ACL_REJECT; 336 } 337 338 query->auth_msg = os_malloc(len); 339 if (query->auth_msg == NULL) { 340 wpa_printf(MSG_ERROR, "Failed to allocate memory for " 341 "auth frame."); 342 hostapd_acl_query_free(query); 343 return HOSTAPD_ACL_REJECT; 344 } 345 os_memcpy(query->auth_msg, msg, len); 346 query->auth_msg_len = len; 347 query->next = hapd->acl_queries; 348 hapd->acl_queries = query; 349 350 /* Queued data will be processed in hostapd_acl_recv_radius() 351 * when RADIUS server replies to the sent Access-Request. */ 352 return HOSTAPD_ACL_PENDING; 353#endif /* CONFIG_NO_RADIUS */ 354 } 355 356 return HOSTAPD_ACL_REJECT; 357} 358 359 360#ifndef CONFIG_NO_RADIUS 361static void hostapd_acl_expire_cache(struct hostapd_data *hapd, 362 struct os_reltime *now) 363{ 364 struct hostapd_cached_radius_acl *prev, *entry, *tmp; 365 366 prev = NULL; 367 entry = hapd->acl_cache; 368 369 while (entry) { 370 if (os_reltime_expired(now, &entry->timestamp, 371 RADIUS_ACL_TIMEOUT)) { 372 wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR 373 " has expired.", MAC2STR(entry->addr)); 374 if (prev) 375 prev->next = entry->next; 376 else 377 hapd->acl_cache = entry->next; 378 hostapd_drv_set_radius_acl_expire(hapd, entry->addr); 379 tmp = entry; 380 entry = entry->next; 381 hostapd_acl_cache_free_entry(tmp); 382 continue; 383 } 384 385 prev = entry; 386 entry = entry->next; 387 } 388} 389 390 391static void hostapd_acl_expire_queries(struct hostapd_data *hapd, 392 struct os_reltime *now) 393{ 394 struct hostapd_acl_query_data *prev, *entry, *tmp; 395 396 prev = NULL; 397 entry = hapd->acl_queries; 398 399 while (entry) { 400 if (os_reltime_expired(now, &entry->timestamp, 401 RADIUS_ACL_TIMEOUT)) { 402 wpa_printf(MSG_DEBUG, "ACL query for " MACSTR 403 " has expired.", MAC2STR(entry->addr)); 404 if (prev) 405 prev->next = entry->next; 406 else 407 hapd->acl_queries = entry->next; 408 409 tmp = entry; 410 entry = entry->next; 411 hostapd_acl_query_free(tmp); 412 continue; 413 } 414 415 prev = entry; 416 entry = entry->next; 417 } 418} 419 420 421/** 422 * hostapd_acl_expire - ACL cache expiration callback 423 * @hapd: struct hostapd_data * 424 */ 425void hostapd_acl_expire(struct hostapd_data *hapd) 426{ 427 struct os_reltime now; 428 429 os_get_reltime(&now); 430 hostapd_acl_expire_cache(hapd, &now); 431 hostapd_acl_expire_queries(hapd, &now); 432} 433 434 435static void decode_tunnel_passwords(struct hostapd_data *hapd, 436 const u8 *shared_secret, 437 size_t shared_secret_len, 438 struct radius_msg *msg, 439 struct radius_msg *req, 440 struct hostapd_cached_radius_acl *cache) 441{ 442 int passphraselen; 443 char *passphrase, *strpassphrase; 444 size_t i; 445 struct hostapd_sta_wpa_psk_short *psk; 446 447 /* 448 * Decode all tunnel passwords as PSK and save them into a linked list. 449 */ 450 for (i = 0; ; i++) { 451 passphrase = radius_msg_get_tunnel_password( 452 msg, &passphraselen, shared_secret, shared_secret_len, 453 req, i); 454 /* 455 * Passphrase is NULL iff there is no i-th Tunnel-Password 456 * attribute in msg. 457 */ 458 if (passphrase == NULL) 459 break; 460 /* 461 * passphrase does not contain the NULL termination. 462 * Add it here as pbkdf2_sha1() requires it. 463 */ 464 strpassphrase = os_zalloc(passphraselen + 1); 465 psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); 466 if (strpassphrase && psk) { 467 os_memcpy(strpassphrase, passphrase, passphraselen); 468 pbkdf2_sha1(strpassphrase, 469 hapd->conf->ssid.ssid, 470 hapd->conf->ssid.ssid_len, 4096, 471 psk->psk, PMK_LEN); 472 psk->next = cache->psk; 473 cache->psk = psk; 474 psk = NULL; 475 } 476 os_free(strpassphrase); 477 os_free(psk); 478 os_free(passphrase); 479 } 480} 481 482 483/** 484 * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages 485 * @msg: RADIUS response message 486 * @req: RADIUS request message 487 * @shared_secret: RADIUS shared secret 488 * @shared_secret_len: Length of shared_secret in octets 489 * @data: Context data (struct hostapd_data *) 490 * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and 491 * was processed here) or RADIUS_RX_UNKNOWN if not. 492 */ 493static RadiusRxResult 494hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, 495 const u8 *shared_secret, size_t shared_secret_len, 496 void *data) 497{ 498 struct hostapd_data *hapd = data; 499 struct hostapd_acl_query_data *query, *prev; 500 struct hostapd_cached_radius_acl *cache; 501 struct radius_hdr *hdr = radius_msg_get_hdr(msg); 502 503 query = hapd->acl_queries; 504 prev = NULL; 505 while (query) { 506 if (query->radius_id == hdr->identifier) 507 break; 508 prev = query; 509 query = query->next; 510 } 511 if (query == NULL) 512 return RADIUS_RX_UNKNOWN; 513 514 wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS " 515 "message (id=%d)", query->radius_id); 516 517 if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { 518 wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have " 519 "correct authenticator - dropped\n"); 520 return RADIUS_RX_INVALID_AUTHENTICATOR; 521 } 522 523 if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && 524 hdr->code != RADIUS_CODE_ACCESS_REJECT) { 525 wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL " 526 "query", hdr->code); 527 return RADIUS_RX_UNKNOWN; 528 } 529 530 /* Insert Accept/Reject info into ACL cache */ 531 cache = os_zalloc(sizeof(*cache)); 532 if (cache == NULL) { 533 wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); 534 goto done; 535 } 536 os_get_reltime(&cache->timestamp); 537 os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); 538 if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { 539 u8 *buf; 540 size_t len; 541 542 if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, 543 &cache->session_timeout) == 0) 544 cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; 545 else 546 cache->accepted = HOSTAPD_ACL_ACCEPT; 547 548 if (radius_msg_get_attr_int32( 549 msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, 550 &cache->acct_interim_interval) == 0 && 551 cache->acct_interim_interval < 60) { 552 wpa_printf(MSG_DEBUG, "Ignored too small " 553 "Acct-Interim-Interval %d for STA " MACSTR, 554 cache->acct_interim_interval, 555 MAC2STR(query->addr)); 556 cache->acct_interim_interval = 0; 557 } 558 559 cache->vlan_id = radius_msg_get_vlanid(msg); 560 561 decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, 562 msg, req, cache); 563 564 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, 565 &buf, &len, NULL) == 0) { 566 cache->identity = os_zalloc(len + 1); 567 if (cache->identity) 568 os_memcpy(cache->identity, buf, len); 569 } 570 if (radius_msg_get_attr_ptr( 571 msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 572 &buf, &len, NULL) == 0) { 573 cache->radius_cui = os_zalloc(len + 1); 574 if (cache->radius_cui) 575 os_memcpy(cache->radius_cui, buf, len); 576 } 577 578 if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && 579 !cache->psk) 580 cache->accepted = HOSTAPD_ACL_REJECT; 581 582 if (cache->vlan_id && 583 !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { 584 hostapd_logger(hapd, query->addr, 585 HOSTAPD_MODULE_RADIUS, 586 HOSTAPD_LEVEL_INFO, 587 "Invalid VLAN ID %d received from RADIUS server", 588 cache->vlan_id); 589 cache->vlan_id = 0; 590 } 591 if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && 592 !cache->vlan_id) 593 cache->accepted = HOSTAPD_ACL_REJECT; 594 } else 595 cache->accepted = HOSTAPD_ACL_REJECT; 596 cache->next = hapd->acl_cache; 597 hapd->acl_cache = cache; 598 599#ifdef CONFIG_DRIVER_RADIUS_ACL 600 hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted, 601 cache->session_timeout); 602#else /* CONFIG_DRIVER_RADIUS_ACL */ 603#ifdef NEED_AP_MLME 604 /* Re-send original authentication frame for 802.11 processing */ 605 wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " 606 "successful RADIUS ACL query"); 607 ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL); 608#endif /* NEED_AP_MLME */ 609#endif /* CONFIG_DRIVER_RADIUS_ACL */ 610 611 done: 612 if (prev == NULL) 613 hapd->acl_queries = query->next; 614 else 615 prev->next = query->next; 616 617 hostapd_acl_query_free(query); 618 619 return RADIUS_RX_PROCESSED; 620} 621#endif /* CONFIG_NO_RADIUS */ 622 623 624/** 625 * hostapd_acl_init: Initialize IEEE 802.11 ACL 626 * @hapd: hostapd BSS data 627 * Returns: 0 on success, -1 on failure 628 */ 629int hostapd_acl_init(struct hostapd_data *hapd) 630{ 631#ifndef CONFIG_NO_RADIUS 632 if (radius_client_register(hapd->radius, RADIUS_AUTH, 633 hostapd_acl_recv_radius, hapd)) 634 return -1; 635#endif /* CONFIG_NO_RADIUS */ 636 637 return 0; 638} 639 640 641/** 642 * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL 643 * @hapd: hostapd BSS data 644 */ 645void hostapd_acl_deinit(struct hostapd_data *hapd) 646{ 647 struct hostapd_acl_query_data *query, *prev; 648 649#ifndef CONFIG_NO_RADIUS 650 hostapd_acl_cache_free(hapd->acl_cache); 651#endif /* CONFIG_NO_RADIUS */ 652 653 query = hapd->acl_queries; 654 while (query) { 655 prev = query; 656 query = query->next; 657 hostapd_acl_query_free(prev); 658 } 659} 660 661 662void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) 663{ 664 while (psk) { 665 struct hostapd_sta_wpa_psk_short *prev = psk; 666 psk = psk->next; 667 os_free(prev); 668 } 669} 670