1/* 2 * Control interface for shared AP commands 3 * Copyright (c) 2004-2014, 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 9#include "utils/includes.h" 10 11#include "utils/common.h" 12#include "common/ieee802_11_defs.h" 13#include "common/sae.h" 14#include "eapol_auth/eapol_auth_sm.h" 15#include "fst/fst_ctrl_iface.h" 16#include "hostapd.h" 17#include "ieee802_1x.h" 18#include "wpa_auth.h" 19#include "ieee802_11.h" 20#include "sta_info.h" 21#include "wps_hostapd.h" 22#include "p2p_hostapd.h" 23#include "ctrl_iface_ap.h" 24#include "ap_drv_ops.h" 25#include "mbo_ap.h" 26#include "taxonomy.h" 27 28 29static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, 30 struct sta_info *sta, 31 char *buf, size_t buflen) 32{ 33 struct hostap_sta_driver_data data; 34 int ret; 35 36 if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) 37 return 0; 38 39 ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" 40 "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n", 41 data.rx_packets, data.tx_packets, 42 data.rx_bytes, data.tx_bytes, data.inactive_msec); 43 if (os_snprintf_error(buflen, ret)) 44 return 0; 45 return ret; 46} 47 48 49static int hostapd_get_sta_conn_time(struct sta_info *sta, 50 char *buf, size_t buflen) 51{ 52 struct os_reltime age; 53 int ret; 54 55 if (!sta->connected_time.sec) 56 return 0; 57 58 os_reltime_age(&sta->connected_time, &age); 59 60 ret = os_snprintf(buf, buflen, "connected_time=%u\n", 61 (unsigned int) age.sec); 62 if (os_snprintf_error(buflen, ret)) 63 return 0; 64 return ret; 65} 66 67 68static const char * timeout_next_str(int val) 69{ 70 switch (val) { 71 case STA_NULLFUNC: 72 return "NULLFUNC POLL"; 73 case STA_DISASSOC: 74 return "DISASSOC"; 75 case STA_DEAUTH: 76 return "DEAUTH"; 77 case STA_REMOVE: 78 return "REMOVE"; 79 case STA_DISASSOC_FROM_CLI: 80 return "DISASSOC_FROM_CLI"; 81 } 82 83 return "?"; 84} 85 86 87static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, 88 struct sta_info *sta, 89 char *buf, size_t buflen) 90{ 91 int len, res, ret, i; 92 93 if (!sta) 94 return 0; 95 96 len = 0; 97 ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", 98 MAC2STR(sta->addr)); 99 if (os_snprintf_error(buflen - len, ret)) 100 return len; 101 len += ret; 102 103 ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); 104 if (ret < 0) 105 return len; 106 len += ret; 107 108 ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" 109 "listen_interval=%d\nsupported_rates=", 110 sta->aid, sta->capability, sta->listen_interval); 111 if (os_snprintf_error(buflen - len, ret)) 112 return len; 113 len += ret; 114 115 for (i = 0; i < sta->supported_rates_len; i++) { 116 ret = os_snprintf(buf + len, buflen - len, "%02x%s", 117 sta->supported_rates[i], 118 i + 1 < sta->supported_rates_len ? " " : ""); 119 if (os_snprintf_error(buflen - len, ret)) 120 return len; 121 len += ret; 122 } 123 124 ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", 125 timeout_next_str(sta->timeout_next)); 126 if (os_snprintf_error(buflen - len, ret)) 127 return len; 128 len += ret; 129 130 res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); 131 if (res >= 0) 132 len += res; 133 res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); 134 if (res >= 0) 135 len += res; 136 res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); 137 if (res >= 0) 138 len += res; 139 res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, 140 buflen - len); 141 if (res >= 0) 142 len += res; 143 res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); 144 if (res >= 0) 145 len += res; 146 147 len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); 148 len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); 149 150#ifdef CONFIG_SAE 151 if (sta->sae && sta->sae->state == SAE_ACCEPTED) { 152 res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n", 153 sta->sae->group); 154 if (!os_snprintf_error(buflen - len, res)) 155 len += res; 156 } 157#endif /* CONFIG_SAE */ 158 159 if (sta->vlan_id > 0) { 160 res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", 161 sta->vlan_id); 162 if (!os_snprintf_error(buflen - len, res)) 163 len += res; 164 } 165 166 res = mbo_ap_get_info(sta, buf + len, buflen - len); 167 if (res >= 0) 168 len += res; 169 170 if (sta->supp_op_classes && 171 buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { 172 len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); 173 len += wpa_snprintf_hex(buf + len, buflen - len, 174 sta->supp_op_classes + 1, 175 sta->supp_op_classes[0]); 176 len += os_snprintf(buf + len, buflen - len, "\n"); 177 } 178 179 return len; 180} 181 182 183int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, 184 char *buf, size_t buflen) 185{ 186 return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); 187} 188 189 190int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, 191 char *buf, size_t buflen) 192{ 193 u8 addr[ETH_ALEN]; 194 int ret; 195 const char *pos; 196 struct sta_info *sta; 197 198 if (hwaddr_aton(txtaddr, addr)) { 199 ret = os_snprintf(buf, buflen, "FAIL\n"); 200 if (os_snprintf_error(buflen, ret)) 201 return 0; 202 return ret; 203 } 204 205 sta = ap_get_sta(hapd, addr); 206 if (sta == NULL) 207 return -1; 208 209 pos = os_strchr(txtaddr, ' '); 210 if (pos) { 211 pos++; 212 213#ifdef HOSTAPD_DUMP_STATE 214 if (os_strcmp(pos, "eapol") == 0) { 215 if (sta->eapol_sm == NULL) 216 return -1; 217 return eapol_auth_dump_state(sta->eapol_sm, buf, 218 buflen); 219 } 220#endif /* HOSTAPD_DUMP_STATE */ 221 222 return -1; 223 } 224 225 ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); 226 ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); 227 228 return ret; 229} 230 231 232int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, 233 char *buf, size_t buflen) 234{ 235 u8 addr[ETH_ALEN]; 236 struct sta_info *sta; 237 int ret; 238 239 if (hwaddr_aton(txtaddr, addr) || 240 (sta = ap_get_sta(hapd, addr)) == NULL) { 241 ret = os_snprintf(buf, buflen, "FAIL\n"); 242 if (os_snprintf_error(buflen, ret)) 243 return 0; 244 return ret; 245 } 246 247 if (!sta->next) 248 return 0; 249 250 return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); 251} 252 253 254#ifdef CONFIG_P2P_MANAGER 255static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, 256 u8 minor_reason_code, const u8 *addr) 257{ 258 struct ieee80211_mgmt *mgmt; 259 int ret; 260 u8 *pos; 261 262 if (!hapd->drv_priv || !hapd->driver->send_frame) 263 return -1; 264 265 mgmt = os_zalloc(sizeof(*mgmt) + 100); 266 if (mgmt == NULL) 267 return -1; 268 269 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); 270 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR 271 " with minor reason code %u (stype=%u (%s))", 272 MAC2STR(addr), minor_reason_code, stype, 273 fc2str(le_to_host16(mgmt->frame_control))); 274 275 os_memcpy(mgmt->da, addr, ETH_ALEN); 276 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 277 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 278 if (stype == WLAN_FC_STYPE_DEAUTH) { 279 mgmt->u.deauth.reason_code = 280 host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 281 pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); 282 } else { 283 mgmt->u.disassoc.reason_code = 284 host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 285 pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); 286 } 287 288 *pos++ = WLAN_EID_VENDOR_SPECIFIC; 289 *pos++ = 4 + 3 + 1; 290 WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE); 291 pos += 4; 292 293 *pos++ = P2P_ATTR_MINOR_REASON_CODE; 294 WPA_PUT_LE16(pos, 1); 295 pos += 2; 296 *pos++ = minor_reason_code; 297 298 ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, 299 pos - (u8 *) mgmt, 1); 300 os_free(mgmt); 301 302 return ret < 0 ? -1 : 0; 303} 304#endif /* CONFIG_P2P_MANAGER */ 305 306 307int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, 308 const char *txtaddr) 309{ 310 u8 addr[ETH_ALEN]; 311 struct sta_info *sta; 312 const char *pos; 313 u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 314 315 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", 316 txtaddr); 317 318 if (hwaddr_aton(txtaddr, addr)) 319 return -1; 320 321 pos = os_strstr(txtaddr, " reason="); 322 if (pos) 323 reason = atoi(pos + 8); 324 325 pos = os_strstr(txtaddr, " test="); 326 if (pos) { 327 struct ieee80211_mgmt mgmt; 328 int encrypt; 329 if (!hapd->drv_priv || !hapd->driver->send_frame) 330 return -1; 331 pos += 6; 332 encrypt = atoi(pos); 333 os_memset(&mgmt, 0, sizeof(mgmt)); 334 mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 335 WLAN_FC_STYPE_DEAUTH); 336 os_memcpy(mgmt.da, addr, ETH_ALEN); 337 os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 338 os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 339 mgmt.u.deauth.reason_code = host_to_le16(reason); 340 if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 341 IEEE80211_HDRLEN + 342 sizeof(mgmt.u.deauth), 343 encrypt) < 0) 344 return -1; 345 return 0; 346 } 347 348#ifdef CONFIG_P2P_MANAGER 349 pos = os_strstr(txtaddr, " p2p="); 350 if (pos) { 351 return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, 352 atoi(pos + 5), addr); 353 } 354#endif /* CONFIG_P2P_MANAGER */ 355 356 if (os_strstr(txtaddr, " tx=0")) 357 hostapd_drv_sta_remove(hapd, addr); 358 else 359 hostapd_drv_sta_deauth(hapd, addr, reason); 360 sta = ap_get_sta(hapd, addr); 361 if (sta) 362 ap_sta_deauthenticate(hapd, sta, reason); 363 else if (addr[0] == 0xff) 364 hostapd_free_stas(hapd); 365 366 return 0; 367} 368 369 370int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, 371 const char *txtaddr) 372{ 373 u8 addr[ETH_ALEN]; 374 struct sta_info *sta; 375 const char *pos; 376 u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 377 378 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", 379 txtaddr); 380 381 if (hwaddr_aton(txtaddr, addr)) 382 return -1; 383 384 pos = os_strstr(txtaddr, " reason="); 385 if (pos) 386 reason = atoi(pos + 8); 387 388 pos = os_strstr(txtaddr, " test="); 389 if (pos) { 390 struct ieee80211_mgmt mgmt; 391 int encrypt; 392 if (!hapd->drv_priv || !hapd->driver->send_frame) 393 return -1; 394 pos += 6; 395 encrypt = atoi(pos); 396 os_memset(&mgmt, 0, sizeof(mgmt)); 397 mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 398 WLAN_FC_STYPE_DISASSOC); 399 os_memcpy(mgmt.da, addr, ETH_ALEN); 400 os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 401 os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 402 mgmt.u.disassoc.reason_code = host_to_le16(reason); 403 if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 404 IEEE80211_HDRLEN + 405 sizeof(mgmt.u.deauth), 406 encrypt) < 0) 407 return -1; 408 return 0; 409 } 410 411#ifdef CONFIG_P2P_MANAGER 412 pos = os_strstr(txtaddr, " p2p="); 413 if (pos) { 414 return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, 415 atoi(pos + 5), addr); 416 } 417#endif /* CONFIG_P2P_MANAGER */ 418 419 if (os_strstr(txtaddr, " tx=0")) 420 hostapd_drv_sta_remove(hapd, addr); 421 else 422 hostapd_drv_sta_disassoc(hapd, addr, reason); 423 sta = ap_get_sta(hapd, addr); 424 if (sta) 425 ap_sta_disassociate(hapd, sta, reason); 426 else if (addr[0] == 0xff) 427 hostapd_free_stas(hapd); 428 429 return 0; 430} 431 432 433#ifdef CONFIG_TAXONOMY 434int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, 435 const char *txtaddr, 436 char *buf, size_t buflen) 437{ 438 u8 addr[ETH_ALEN]; 439 struct sta_info *sta; 440 441 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); 442 443 if (hwaddr_aton(txtaddr, addr)) 444 return -1; 445 446 sta = ap_get_sta(hapd, addr); 447 if (!sta) 448 return -1; 449 450 return retrieve_sta_taxonomy(hapd, sta, buf, buflen); 451} 452#endif /* CONFIG_TAXONOMY */ 453 454 455int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, 456 const char *txtaddr) 457{ 458 u8 addr[ETH_ALEN]; 459 struct sta_info *sta; 460 461 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); 462 463 if (hwaddr_aton(txtaddr, addr)) 464 return -1; 465 466 sta = ap_get_sta(hapd, addr); 467 if (!sta) 468 return -1; 469 470 hostapd_drv_poll_client(hapd, hapd->own_addr, addr, 471 sta->flags & WLAN_STA_WMM); 472 return 0; 473} 474 475 476int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, 477 size_t buflen) 478{ 479 struct hostapd_iface *iface = hapd->iface; 480 int len = 0, ret; 481 size_t i; 482 483 ret = os_snprintf(buf + len, buflen - len, 484 "state=%s\n" 485 "phy=%s\n" 486 "freq=%d\n" 487 "num_sta_non_erp=%d\n" 488 "num_sta_no_short_slot_time=%d\n" 489 "num_sta_no_short_preamble=%d\n" 490 "olbc=%d\n" 491 "num_sta_ht_no_gf=%d\n" 492 "num_sta_no_ht=%d\n" 493 "num_sta_ht_20_mhz=%d\n" 494 "num_sta_ht40_intolerant=%d\n" 495 "olbc_ht=%d\n" 496 "ht_op_mode=0x%x\n", 497 hostapd_state_text(iface->state), 498 iface->phy, 499 iface->freq, 500 iface->num_sta_non_erp, 501 iface->num_sta_no_short_slot_time, 502 iface->num_sta_no_short_preamble, 503 iface->olbc, 504 iface->num_sta_ht_no_gf, 505 iface->num_sta_no_ht, 506 iface->num_sta_ht_20mhz, 507 iface->num_sta_ht40_intolerant, 508 iface->olbc_ht, 509 iface->ht_op_mode); 510 if (os_snprintf_error(buflen - len, ret)) 511 return len; 512 len += ret; 513 514 if (!iface->cac_started || !iface->dfs_cac_ms) { 515 ret = os_snprintf(buf + len, buflen - len, 516 "cac_time_seconds=%d\n" 517 "cac_time_left_seconds=N/A\n", 518 iface->dfs_cac_ms / 1000); 519 } else { 520 /* CAC started and CAC time set - calculate remaining time */ 521 struct os_reltime now; 522 unsigned int left_time; 523 524 os_reltime_age(&iface->dfs_cac_start, &now); 525 left_time = iface->dfs_cac_ms / 1000 - now.sec; 526 ret = os_snprintf(buf + len, buflen - len, 527 "cac_time_seconds=%u\n" 528 "cac_time_left_seconds=%u\n", 529 iface->dfs_cac_ms / 1000, 530 left_time); 531 } 532 if (os_snprintf_error(buflen - len, ret)) 533 return len; 534 len += ret; 535 536 ret = os_snprintf(buf + len, buflen - len, 537 "channel=%u\n" 538 "secondary_channel=%d\n" 539 "ieee80211n=%d\n" 540 "ieee80211ac=%d\n", 541 iface->conf->channel, 542 iface->conf->ieee80211n && !hapd->conf->disable_11n ? 543 iface->conf->secondary_channel : 0, 544 iface->conf->ieee80211n && !hapd->conf->disable_11n, 545 iface->conf->ieee80211ac && 546 !hapd->conf->disable_11ac); 547 if (os_snprintf_error(buflen - len, ret)) 548 return len; 549 len += ret; 550 if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { 551 ret = os_snprintf(buf + len, buflen - len, 552 "vht_oper_chwidth=%d\n" 553 "vht_oper_centr_freq_seg0_idx=%d\n" 554 "vht_oper_centr_freq_seg1_idx=%d\n", 555 iface->conf->vht_oper_chwidth, 556 iface->conf->vht_oper_centr_freq_seg0_idx, 557 iface->conf->vht_oper_centr_freq_seg1_idx); 558 if (os_snprintf_error(buflen - len, ret)) 559 return len; 560 len += ret; 561 } 562 563 for (i = 0; i < iface->num_bss; i++) { 564 struct hostapd_data *bss = iface->bss[i]; 565 ret = os_snprintf(buf + len, buflen - len, 566 "bss[%d]=%s\n" 567 "bssid[%d]=" MACSTR "\n" 568 "ssid[%d]=%s\n" 569 "num_sta[%d]=%d\n", 570 (int) i, bss->conf->iface, 571 (int) i, MAC2STR(bss->own_addr), 572 (int) i, 573 wpa_ssid_txt(bss->conf->ssid.ssid, 574 bss->conf->ssid.ssid_len), 575 (int) i, bss->num_sta); 576 if (os_snprintf_error(buflen - len, ret)) 577 return len; 578 len += ret; 579 } 580 581 return len; 582} 583 584 585int hostapd_parse_csa_settings(const char *pos, 586 struct csa_settings *settings) 587{ 588 char *end; 589 590 os_memset(settings, 0, sizeof(*settings)); 591 settings->cs_count = strtol(pos, &end, 10); 592 if (pos == end) { 593 wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); 594 return -1; 595 } 596 597 settings->freq_params.freq = atoi(end); 598 if (settings->freq_params.freq == 0) { 599 wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); 600 return -1; 601 } 602 603#define SET_CSA_SETTING(str) \ 604 do { \ 605 const char *pos2 = os_strstr(pos, " " #str "="); \ 606 if (pos2) { \ 607 pos2 += sizeof(" " #str "=") - 1; \ 608 settings->freq_params.str = atoi(pos2); \ 609 } \ 610 } while (0) 611 612 SET_CSA_SETTING(center_freq1); 613 SET_CSA_SETTING(center_freq2); 614 SET_CSA_SETTING(bandwidth); 615 SET_CSA_SETTING(sec_channel_offset); 616 settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); 617 settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); 618 settings->block_tx = !!os_strstr(pos, " blocktx"); 619#undef SET_CSA_SETTING 620 621 return 0; 622} 623 624 625int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) 626{ 627 return hostapd_drv_stop_ap(hapd); 628} 629 630 631int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, 632 size_t len) 633{ 634 return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); 635} 636 637 638void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) 639{ 640 wpa_auth_pmksa_flush(hapd->wpa_auth); 641} 642 643 644#ifdef CONFIG_PMKSA_CACHE_EXTERNAL 645#ifdef CONFIG_MESH 646 647int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, 648 const u8 *addr, char *buf, size_t len) 649{ 650 return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); 651} 652 653 654void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) 655{ 656 u8 spa[ETH_ALEN]; 657 u8 pmkid[PMKID_LEN]; 658 u8 pmk[PMK_LEN_MAX]; 659 char *pos; 660 int expiration; 661 662 /* 663 * Entry format: 664 * <BSSID> <PMKID> <PMK> <expiration in seconds> 665 */ 666 667 if (hwaddr_aton(cmd, spa)) 668 return NULL; 669 670 pos = os_strchr(cmd, ' '); 671 if (!pos) 672 return NULL; 673 pos++; 674 675 if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) 676 return NULL; 677 678 pos = os_strchr(pos, ' '); 679 if (!pos) 680 return NULL; 681 pos++; 682 683 if (hexstr2bin(pos, pmk, PMK_LEN) < 0) 684 return NULL; 685 686 pos = os_strchr(pos, ' '); 687 if (!pos) 688 return NULL; 689 pos++; 690 691 if (sscanf(pos, "%d", &expiration) != 1) 692 return NULL; 693 694 return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); 695} 696 697#endif /* CONFIG_MESH */ 698#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ 699