| /* |
| * WPA Supplicant / Control interface (shared code for all backends) |
| * Copyright (c) 2004-2008, Jouni Malinen <[email protected]> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * Alternatively, this software may be distributed under the terms of BSD |
| * license. |
| * |
| * See README and COPYING for more details. |
| */ |
| |
| #include "includes.h" |
| |
| #include "common.h" |
| #include "eloop.h" |
| #include "wpa.h" |
| #include "config.h" |
| #include "eapol_supp/eapol_supp_sm.h" |
| #include "wpa_supplicant_i.h" |
| #include "ctrl_iface.h" |
| #include "l2_packet/l2_packet.h" |
| #include "preauth.h" |
| #include "pmksa_cache.h" |
| #include "wpa_ctrl.h" |
| #include "eap_peer/eap.h" |
| #include "ieee802_11_defs.h" |
| #include "wps_supplicant.h" |
| #include "wps/wps.h" |
| |
| static int wpa_supplicant_global_iface_list(struct wpa_global *global, |
| char *buf, int len); |
| static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, |
| char *buf, int len); |
| |
| |
| static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, |
| char *cmd) |
| { |
| char *value; |
| int ret = 0; |
| |
| value = os_strchr(cmd, ' '); |
| if (value == NULL) |
| return -1; |
| *value++ = '\0'; |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); |
| if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { |
| eapol_sm_configure(wpa_s->eapol, |
| atoi(value), -1, -1, -1); |
| } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) { |
| eapol_sm_configure(wpa_s->eapol, |
| -1, atoi(value), -1, -1); |
| } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) { |
| eapol_sm_configure(wpa_s->eapol, |
| -1, -1, atoi(value), -1); |
| } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) { |
| eapol_sm_configure(wpa_s->eapol, |
| -1, -1, -1, atoi(value)); |
| } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { |
| if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, |
| atoi(value))) |
| ret = -1; |
| } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == |
| 0) { |
| if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, |
| atoi(value))) |
| ret = -1; |
| } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { |
| if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) |
| ret = -1; |
| } else |
| ret = -1; |
| |
| return ret; |
| } |
| |
| |
| #ifdef IEEE8021X_EAPOL |
| static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, |
| char *addr) |
| { |
| u8 bssid[ETH_ALEN]; |
| struct wpa_ssid *ssid = wpa_s->current_ssid; |
| |
| if (hwaddr_aton(addr, bssid)) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " |
| "'%s'", addr); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); |
| rsn_preauth_deinit(wpa_s->wpa); |
| if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL)) |
| return -1; |
| |
| return 0; |
| } |
| #endif /* IEEE8021X_EAPOL */ |
| |
| |
| #ifdef CONFIG_PEERKEY |
| /* MLME-STKSTART.request(peer) */ |
| static int wpa_supplicant_ctrl_iface_stkstart( |
| struct wpa_supplicant *wpa_s, char *addr) |
| { |
| u8 peer[ETH_ALEN]; |
| |
| if (hwaddr_aton(addr, peer)) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid " |
| "address '%s'", peer); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR, |
| MAC2STR(peer)); |
| |
| return wpa_sm_stkstart(wpa_s->wpa, peer); |
| } |
| #endif /* CONFIG_PEERKEY */ |
| |
| |
| #ifdef CONFIG_IEEE80211R |
| static int wpa_supplicant_ctrl_iface_ft_ds( |
| struct wpa_supplicant *wpa_s, char *addr) |
| { |
| u8 target_ap[ETH_ALEN]; |
| |
| if (hwaddr_aton(addr, target_ap)) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid " |
| "address '%s'", target_ap); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap)); |
| |
| return wpa_ft_start_over_ds(wpa_s->wpa, target_ap); |
| } |
| #endif /* CONFIG_IEEE80211R */ |
| |
| |
| #ifdef CONFIG_WPS |
| static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, |
| char *cmd) |
| { |
| u8 bssid[ETH_ALEN]; |
| |
| if (cmd == NULL || os_strcmp(cmd, "any") == 0) |
| return wpas_wps_start_pbc(wpa_s, NULL); |
| |
| if (hwaddr_aton(cmd, bssid)) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", |
| cmd); |
| return -1; |
| } |
| |
| return wpas_wps_start_pbc(wpa_s, bssid); |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, |
| char *cmd, char *buf, |
| size_t buflen) |
| { |
| u8 bssid[ETH_ALEN], *_bssid = bssid; |
| char *pin; |
| int ret; |
| |
| pin = os_strchr(cmd, ' '); |
| if (pin) |
| *pin++ = '\0'; |
| |
| if (os_strcmp(cmd, "any") == 0) |
| _bssid = NULL; |
| else if (hwaddr_aton(cmd, bssid)) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", |
| cmd); |
| return -1; |
| } |
| |
| if (pin) { |
| ret = wpas_wps_start_pin(wpa_s, _bssid, pin); |
| if (ret < 0) |
| return -1; |
| ret = os_snprintf(buf, buflen, "%s", pin); |
| if (ret < 0 || (size_t) ret >= buflen) |
| return -1; |
| return ret; |
| } |
| |
| ret = wpas_wps_start_pin(wpa_s, _bssid, NULL); |
| if (ret < 0) |
| return -1; |
| |
| /* Return the generated PIN */ |
| ret = os_snprintf(buf, buflen, "%08d", ret); |
| if (ret < 0 || (size_t) ret >= buflen) |
| return -1; |
| return ret; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, |
| char *cmd) |
| { |
| u8 bssid[ETH_ALEN], *_bssid = bssid; |
| char *pin; |
| |
| pin = os_strchr(cmd, ' '); |
| if (pin == NULL) |
| return -1; |
| *pin++ = '\0'; |
| |
| if (os_strcmp(cmd, "any") == 0) |
| _bssid = NULL; |
| else if (hwaddr_aton(cmd, bssid)) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", |
| cmd); |
| return -1; |
| } |
| |
| return wpas_wps_start_reg(wpa_s, _bssid, pin); |
| } |
| #endif /* CONFIG_WPS */ |
| |
| |
| static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, |
| char *rsp) |
| { |
| #ifdef IEEE8021X_EAPOL |
| char *pos, *id_pos; |
| int id; |
| struct wpa_ssid *ssid; |
| struct eap_peer_config *eap; |
| |
| pos = os_strchr(rsp, '-'); |
| if (pos == NULL) |
| return -1; |
| *pos++ = '\0'; |
| id_pos = pos; |
| pos = os_strchr(pos, ':'); |
| if (pos == NULL) |
| return -1; |
| *pos++ = '\0'; |
| id = atoi(id_pos); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); |
| wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", |
| (u8 *) pos, os_strlen(pos)); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " |
| "to update", id); |
| return -1; |
| } |
| eap = &ssid->eap; |
| |
| if (os_strcmp(rsp, "IDENTITY") == 0) { |
| os_free(eap->identity); |
| eap->identity = (u8 *) os_strdup(pos); |
| eap->identity_len = os_strlen(pos); |
| eap->pending_req_identity = 0; |
| if (ssid == wpa_s->current_ssid) |
| wpa_s->reassociate = 1; |
| } else if (os_strcmp(rsp, "PASSWORD") == 0) { |
| os_free(eap->password); |
| eap->password = (u8 *) os_strdup(pos); |
| eap->password_len = os_strlen(pos); |
| eap->pending_req_password = 0; |
| if (ssid == wpa_s->current_ssid) |
| wpa_s->reassociate = 1; |
| } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) { |
| os_free(eap->new_password); |
| eap->new_password = (u8 *) os_strdup(pos); |
| eap->new_password_len = os_strlen(pos); |
| eap->pending_req_new_password = 0; |
| if (ssid == wpa_s->current_ssid) |
| wpa_s->reassociate = 1; |
| } else if (os_strcmp(rsp, "PIN") == 0) { |
| os_free(eap->pin); |
| eap->pin = os_strdup(pos); |
| eap->pending_req_pin = 0; |
| if (ssid == wpa_s->current_ssid) |
| wpa_s->reassociate = 1; |
| } else if (os_strcmp(rsp, "OTP") == 0) { |
| os_free(eap->otp); |
| eap->otp = (u8 *) os_strdup(pos); |
| eap->otp_len = os_strlen(pos); |
| os_free(eap->pending_req_otp); |
| eap->pending_req_otp = NULL; |
| eap->pending_req_otp_len = 0; |
| } else if (os_strcmp(rsp, "PASSPHRASE") == 0) { |
| os_free(eap->private_key_passwd); |
| eap->private_key_passwd = (u8 *) os_strdup(pos); |
| eap->pending_req_passphrase = 0; |
| if (ssid == wpa_s->current_ssid) |
| wpa_s->reassociate = 1; |
| } else { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp); |
| return -1; |
| } |
| |
| return 0; |
| #else /* IEEE8021X_EAPOL */ |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); |
| return -1; |
| #endif /* IEEE8021X_EAPOL */ |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, |
| const char *params, |
| char *buf, size_t buflen) |
| { |
| char *pos, *end, tmp[30]; |
| int res, verbose, ret; |
| |
| verbose = os_strcmp(params, "-VERBOSE") == 0; |
| pos = buf; |
| end = buf + buflen; |
| if (wpa_s->wpa_state >= WPA_ASSOCIATED) { |
| struct wpa_ssid *ssid = wpa_s->current_ssid; |
| ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", |
| MAC2STR(wpa_s->bssid)); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| if (ssid) { |
| u8 *_ssid = ssid->ssid; |
| size_t ssid_len = ssid->ssid_len; |
| u8 ssid_buf[MAX_SSID_LEN]; |
| if (ssid_len == 0) { |
| int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); |
| if (_res < 0) |
| ssid_len = 0; |
| else |
| ssid_len = _res; |
| _ssid = ssid_buf; |
| } |
| ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", |
| wpa_ssid_txt(_ssid, ssid_len), |
| ssid->id); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| if (ssid->id_str) { |
| ret = os_snprintf(pos, end - pos, |
| "id_str=%s\n", |
| ssid->id_str); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| } |
| |
| pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); |
| } |
| ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", |
| wpa_supplicant_state_txt(wpa_s->wpa_state)); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| if (wpa_s->l2 && |
| l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { |
| ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || |
| wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { |
| res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, |
| verbose); |
| if (res >= 0) |
| pos += res; |
| } |
| |
| res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); |
| if (res >= 0) |
| pos += res; |
| |
| return pos - buf; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, |
| char *cmd) |
| { |
| char *pos; |
| int id; |
| struct wpa_ssid *ssid; |
| u8 bssid[ETH_ALEN]; |
| |
| /* cmd: "<network id> <BSSID>" */ |
| pos = os_strchr(cmd, ' '); |
| if (pos == NULL) |
| return -1; |
| *pos++ = '\0'; |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos); |
| if (hwaddr_aton(pos, bssid)) { |
| wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos); |
| return -1; |
| } |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " |
| "to update", id); |
| return -1; |
| } |
| |
| os_memcpy(ssid->bssid, bssid, ETH_ALEN); |
| ssid->bssid_set = !is_zero_ether_addr(bssid); |
| |
| return 0; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_list_networks( |
| struct wpa_supplicant *wpa_s, char *buf, size_t buflen) |
| { |
| char *pos, *end; |
| struct wpa_ssid *ssid; |
| int ret; |
| |
| pos = buf; |
| end = buf + buflen; |
| ret = os_snprintf(pos, end - pos, |
| "network id / ssid / bssid / flags\n"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ssid = wpa_s->conf->ssid; |
| while (ssid) { |
| ret = os_snprintf(pos, end - pos, "%d\t%s", |
| ssid->id, |
| wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| if (ssid->bssid_set) { |
| ret = os_snprintf(pos, end - pos, "\t" MACSTR, |
| MAC2STR(ssid->bssid)); |
| } else { |
| ret = os_snprintf(pos, end - pos, "\tany"); |
| } |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| ret = os_snprintf(pos, end - pos, "\t%s%s", |
| ssid == wpa_s->current_ssid ? |
| "[CURRENT]" : "", |
| ssid->disabled ? "[DISABLED]" : ""); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| ret = os_snprintf(pos, end - pos, "\n"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ssid = ssid->next; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) |
| { |
| int first = 1, ret; |
| ret = os_snprintf(pos, end - pos, "-"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| if (cipher & WPA_CIPHER_NONE) { |
| ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (cipher & WPA_CIPHER_WEP40) { |
| ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (cipher & WPA_CIPHER_WEP104) { |
| ret = os_snprintf(pos, end - pos, "%sWEP104", |
| first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (cipher & WPA_CIPHER_TKIP) { |
| ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (cipher & WPA_CIPHER_CCMP) { |
| ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| return pos; |
| } |
| |
| |
| static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, |
| const u8 *ie, size_t ie_len) |
| { |
| struct wpa_ie_data data; |
| int first, ret; |
| |
| ret = os_snprintf(pos, end - pos, "[%s-", proto); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| |
| if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { |
| ret = os_snprintf(pos, end - pos, "?]"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| return pos; |
| } |
| |
| first = 1; |
| if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { |
| ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (data.key_mgmt & WPA_KEY_MGMT_PSK) { |
| ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { |
| ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| #ifdef CONFIG_IEEE80211R |
| if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { |
| ret = os_snprintf(pos, end - pos, "%sFT/EAP", |
| first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) { |
| ret = os_snprintf(pos, end - pos, "%sFT/PSK", |
| first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| #endif /* CONFIG_IEEE80211R */ |
| #ifdef CONFIG_IEEE80211W |
| if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { |
| ret = os_snprintf(pos, end - pos, "%sEAP-SHA256", |
| first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { |
| ret = os_snprintf(pos, end - pos, "%sPSK-SHA256", |
| first ? "" : "+"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| first = 0; |
| } |
| #endif /* CONFIG_IEEE80211W */ |
| |
| pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); |
| |
| if (data.capabilities & WPA_CAPABILITY_PREAUTH) { |
| ret = os_snprintf(pos, end - pos, "-preauth"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| } |
| |
| ret = os_snprintf(pos, end - pos, "]"); |
| if (ret < 0 || ret >= end - pos) |
| return pos; |
| pos += ret; |
| |
| return pos; |
| } |
| |
| static char * wpa_supplicant_wps_ie_txt(char *pos, char *end, |
| const struct wpa_scan_res *res) |
| { |
| #ifdef CONFIG_WPS |
| struct wpabuf *wps_ie; |
| int ret; |
| const char *txt; |
| |
| wps_ie = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); |
| if (wps_ie == NULL) |
| return pos; |
| |
| if (wps_is_selected_pbc_registrar(wps_ie)) |
| txt = "[WPS-PBC]"; |
| else if (wps_is_selected_pin_registrar(wps_ie)) |
| txt = "[WPS-PIN]"; |
| else |
| txt = "[WPS]"; |
| |
| ret = os_snprintf(pos, end - pos, "%s", txt); |
| if (ret >= 0 && ret < end - pos) |
| pos += ret; |
| wpabuf_free(wps_ie); |
| #endif /* CONFIG_WPS */ |
| |
| return pos; |
| } |
| |
| |
| /* Format one result on one text line into a buffer. */ |
| static int wpa_supplicant_ctrl_iface_scan_result( |
| const struct wpa_scan_res *res, char *buf, size_t buflen) |
| { |
| char *pos, *end; |
| int ret; |
| const u8 *ie, *ie2; |
| |
| pos = buf; |
| end = buf + buflen; |
| |
| ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", |
| MAC2STR(res->bssid), res->freq, res->level); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| ie = wpa_scan_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); |
| if (ie) |
| pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); |
| ie2 = wpa_scan_get_ie(res, WLAN_EID_RSN); |
| if (ie2) |
| pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); |
| pos = wpa_supplicant_wps_ie_txt(pos, end, res); |
| if (!ie && !ie2 && res->caps & IEEE80211_CAP_PRIVACY) { |
| ret = os_snprintf(pos, end - pos, "[WEP]"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| if (res->caps & IEEE80211_CAP_IBSS) { |
| ret = os_snprintf(pos, end - pos, "[IBSS]"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| ie = wpa_scan_get_ie(res, WLAN_EID_SSID); |
| ret = os_snprintf(pos, end - pos, "\t%s", |
| ie ? wpa_ssid_txt(ie + 2, ie[1]) : ""); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ret = os_snprintf(pos, end - pos, "\n"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| return pos - buf; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_scan_results( |
| struct wpa_supplicant *wpa_s, char *buf, size_t buflen) |
| { |
| char *pos, *end; |
| struct wpa_scan_res *res; |
| int ret; |
| size_t i; |
| |
| if (wpa_s->scan_res == NULL && |
| wpa_supplicant_get_scan_results(wpa_s) < 0) |
| return 0; |
| |
| pos = buf; |
| end = buf + buflen; |
| ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " |
| "flags / ssid\n"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| for (i = 0; i < wpa_s->scan_res->num; i++) { |
| res = wpa_s->scan_res->res[i]; |
| ret = wpa_supplicant_ctrl_iface_scan_result(res, pos, |
| end - pos); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_select_network( |
| struct wpa_supplicant *wpa_s, char *cmd) |
| { |
| int id; |
| struct wpa_ssid *ssid; |
| |
| /* cmd: "<network id>" or "any" */ |
| if (os_strcmp(cmd, "any") == 0) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); |
| ssid = wpa_s->conf->ssid; |
| while (ssid) { |
| ssid->disabled = 0; |
| ssid = ssid->next; |
| } |
| wpa_s->reassociate = 1; |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| return 0; |
| } |
| |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " |
| "id=%d", id); |
| return -1; |
| } |
| |
| if (ssid != wpa_s->current_ssid && wpa_s->current_ssid) |
| wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); |
| |
| /* Mark all other networks disabled and trigger reassociation */ |
| ssid = wpa_s->conf->ssid; |
| while (ssid) { |
| ssid->disabled = id != ssid->id; |
| ssid = ssid->next; |
| } |
| wpa_s->reassociate = 1; |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| |
| return 0; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_enable_network( |
| struct wpa_supplicant *wpa_s, char *cmd) |
| { |
| int id; |
| struct wpa_ssid *ssid; |
| |
| /* cmd: "<network id>" or "all" */ |
| if (os_strcmp(cmd, "all") == 0) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all"); |
| ssid = wpa_s->conf->ssid; |
| while (ssid) { |
| if (ssid == wpa_s->current_ssid && ssid->disabled) |
| wpa_s->reassociate = 1; |
| ssid->disabled = 0; |
| ssid = ssid->next; |
| } |
| if (wpa_s->reassociate) |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| return 0; |
| } |
| |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " |
| "id=%d", id); |
| return -1; |
| } |
| |
| if (wpa_s->current_ssid == NULL && ssid->disabled) { |
| /* |
| * Try to reassociate since there is no current configuration |
| * and a new network was made available. */ |
| wpa_s->reassociate = 1; |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| } |
| ssid->disabled = 0; |
| |
| return 0; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_disable_network( |
| struct wpa_supplicant *wpa_s, char *cmd) |
| { |
| int id; |
| struct wpa_ssid *ssid; |
| |
| /* cmd: "<network id>" or "all" */ |
| if (os_strcmp(cmd, "all") == 0) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all"); |
| ssid = wpa_s->conf->ssid; |
| while (ssid) { |
| ssid->disabled = 1; |
| ssid = ssid->next; |
| } |
| if (wpa_s->current_ssid) |
| wpa_supplicant_disassociate(wpa_s, |
| WLAN_REASON_DEAUTH_LEAVING); |
| return 0; |
| } |
| |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " |
| "id=%d", id); |
| return -1; |
| } |
| |
| if (ssid == wpa_s->current_ssid) |
| wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); |
| ssid->disabled = 1; |
| |
| return 0; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_add_network( |
| struct wpa_supplicant *wpa_s, char *buf, size_t buflen) |
| { |
| struct wpa_ssid *ssid; |
| int ret; |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); |
| |
| ssid = wpa_config_add_network(wpa_s->conf); |
| if (ssid == NULL) |
| return -1; |
| ssid->disabled = 1; |
| wpa_config_set_network_defaults(ssid); |
| |
| ret = os_snprintf(buf, buflen, "%d\n", ssid->id); |
| if (ret < 0 || (size_t) ret >= buflen) |
| return -1; |
| return ret; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_remove_network( |
| struct wpa_supplicant *wpa_s, char *cmd) |
| { |
| int id; |
| struct wpa_ssid *ssid; |
| |
| /* cmd: "<network id>" or "all" */ |
| if (os_strcmp(cmd, "all") == 0) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); |
| ssid = wpa_s->conf->ssid; |
| while (ssid) { |
| id = ssid->id; |
| ssid = ssid->next; |
| wpa_config_remove_network(wpa_s->conf, id); |
| } |
| if (wpa_s->current_ssid) { |
| eapol_sm_invalidate_cached_session(wpa_s->eapol); |
| wpa_supplicant_disassociate(wpa_s, |
| WLAN_REASON_DEAUTH_LEAVING); |
| } |
| return 0; |
| } |
| |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL || |
| wpa_config_remove_network(wpa_s->conf, id) < 0) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " |
| "id=%d", id); |
| return -1; |
| } |
| |
| if (ssid == wpa_s->current_ssid) { |
| /* |
| * Invalidate the EAP session cache if the current network is |
| * removed. |
| */ |
| eapol_sm_invalidate_cached_session(wpa_s->eapol); |
| |
| wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); |
| } |
| |
| return 0; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_set_network( |
| struct wpa_supplicant *wpa_s, char *cmd) |
| { |
| int id; |
| struct wpa_ssid *ssid; |
| char *name, *value; |
| |
| /* cmd: "<network id> <variable name> <value>" */ |
| name = os_strchr(cmd, ' '); |
| if (name == NULL) |
| return -1; |
| *name++ = '\0'; |
| |
| value = os_strchr(name, ' '); |
| if (value == NULL) |
| return -1; |
| *value++ = '\0'; |
| |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'", |
| id, name); |
| wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", |
| (u8 *) value, os_strlen(value)); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " |
| "id=%d", id); |
| return -1; |
| } |
| |
| if (wpa_config_set(ssid, name, value, 0) < 0) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " |
| "variable '%s'", name); |
| return -1; |
| } |
| |
| if (wpa_s->current_ssid == ssid) { |
| /* |
| * Invalidate the EAP session cache if anything in the current |
| * configuration changes. |
| */ |
| eapol_sm_invalidate_cached_session(wpa_s->eapol); |
| } |
| |
| if ((os_strcmp(name, "psk") == 0 && |
| value[0] == '"' && ssid->ssid_len) || |
| (os_strcmp(name, "ssid") == 0 && ssid->passphrase)) |
| wpa_config_update_psk(ssid); |
| |
| return 0; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_get_network( |
| struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) |
| { |
| int id; |
| size_t res; |
| struct wpa_ssid *ssid; |
| char *name, *value; |
| |
| /* cmd: "<network id> <variable name>" */ |
| name = os_strchr(cmd, ' '); |
| if (name == NULL || buflen == 0) |
| return -1; |
| *name++ = '\0'; |
| |
| id = atoi(cmd); |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", |
| id, name); |
| |
| ssid = wpa_config_get_network(wpa_s->conf, id); |
| if (ssid == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " |
| "id=%d", id); |
| return -1; |
| } |
| |
| value = wpa_config_get_no_key(ssid, name); |
| if (value == NULL) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " |
| "variable '%s'", name); |
| return -1; |
| } |
| |
| res = os_strlcpy(buf, value, buflen); |
| if (res >= buflen) { |
| os_free(value); |
| return -1; |
| } |
| |
| os_free(value); |
| |
| return res; |
| } |
| |
| |
| #ifndef CONFIG_NO_CONFIG_WRITE |
| static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) |
| { |
| int ret; |
| |
| if (!wpa_s->conf->update_config) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed " |
| "to update configuration (update_config=0)"); |
| return -1; |
| } |
| |
| ret = wpa_config_write(wpa_s->confname, wpa_s->conf); |
| if (ret) { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to " |
| "update configuration"); |
| } else { |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration" |
| " updated"); |
| } |
| |
| return ret; |
| } |
| #endif /* CONFIG_NO_CONFIG_WRITE */ |
| |
| |
| static int ctrl_iface_get_capability_pairwise(int res, char *strict, |
| struct wpa_driver_capa *capa, |
| char *buf, size_t buflen) |
| { |
| int ret, first = 1; |
| char *pos, *end; |
| size_t len; |
| |
| pos = buf; |
| end = pos + buflen; |
| |
| if (res < 0) { |
| if (strict) |
| return 0; |
| len = os_strlcpy(buf, "CCMP TKIP NONE", buflen); |
| if (len >= buflen) |
| return -1; |
| return len; |
| } |
| |
| if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { |
| ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { |
| ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { |
| ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static int ctrl_iface_get_capability_group(int res, char *strict, |
| struct wpa_driver_capa *capa, |
| char *buf, size_t buflen) |
| { |
| int ret, first = 1; |
| char *pos, *end; |
| size_t len; |
| |
| pos = buf; |
| end = pos + buflen; |
| |
| if (res < 0) { |
| if (strict) |
| return 0; |
| len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen); |
| if (len >= buflen) |
| return -1; |
| return len; |
| } |
| |
| if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { |
| ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { |
| ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) { |
| ret = os_snprintf(pos, end - pos, "%sWEP104", |
| first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) { |
| ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, |
| struct wpa_driver_capa *capa, |
| char *buf, size_t buflen) |
| { |
| int ret; |
| char *pos, *end; |
| size_t len; |
| |
| pos = buf; |
| end = pos + buflen; |
| |
| if (res < 0) { |
| if (strict) |
| return 0; |
| len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE " |
| "NONE", buflen); |
| if (len >= buflen) |
| return -1; |
| return len; |
| } |
| |
| ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | |
| WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { |
| ret = os_snprintf(pos, end - pos, " WPA-EAP"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | |
| WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { |
| ret = os_snprintf(pos, end - pos, " WPA-PSK"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { |
| ret = os_snprintf(pos, end - pos, " WPA-NONE"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static int ctrl_iface_get_capability_proto(int res, char *strict, |
| struct wpa_driver_capa *capa, |
| char *buf, size_t buflen) |
| { |
| int ret, first = 1; |
| char *pos, *end; |
| size_t len; |
| |
| pos = buf; |
| end = pos + buflen; |
| |
| if (res < 0) { |
| if (strict) |
| return 0; |
| len = os_strlcpy(buf, "RSN WPA", buflen); |
| if (len >= buflen) |
| return -1; |
| return len; |
| } |
| |
| if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | |
| WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { |
| ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | |
| WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { |
| ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static int ctrl_iface_get_capability_auth_alg(int res, char *strict, |
| struct wpa_driver_capa *capa, |
| char *buf, size_t buflen) |
| { |
| int ret, first = 1; |
| char *pos, *end; |
| size_t len; |
| |
| pos = buf; |
| end = pos + buflen; |
| |
| if (res < 0) { |
| if (strict) |
| return 0; |
| len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen); |
| if (len >= buflen) |
| return -1; |
| return len; |
| } |
| |
| if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { |
| ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) { |
| ret = os_snprintf(pos, end - pos, "%sSHARED", |
| first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) { |
| ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " "); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| first = 0; |
| } |
| |
| return pos - buf; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_get_capability( |
| struct wpa_supplicant *wpa_s, const char *_field, char *buf, |
| size_t buflen) |
| { |
| struct wpa_driver_capa capa; |
| int res; |
| char *strict; |
| char field[30]; |
| size_t len; |
| |
| /* Determine whether or not strict checking was requested */ |
| len = os_strlcpy(field, _field, sizeof(field)); |
| if (len >= sizeof(field)) |
| return -1; |
| strict = os_strchr(field, ' '); |
| if (strict != NULL) { |
| *strict++ = '\0'; |
| if (os_strcmp(strict, "strict") != 0) |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s", |
| field, strict ? strict : ""); |
| |
| if (os_strcmp(field, "eap") == 0) { |
| return eap_get_names(buf, buflen); |
| } |
| |
| res = wpa_drv_get_capa(wpa_s, &capa); |
| |
| if (os_strcmp(field, "pairwise") == 0) |
| return ctrl_iface_get_capability_pairwise(res, strict, &capa, |
| buf, buflen); |
| |
| if (os_strcmp(field, "group") == 0) |
| return ctrl_iface_get_capability_group(res, strict, &capa, |
| buf, buflen); |
| |
| if (os_strcmp(field, "key_mgmt") == 0) |
| return ctrl_iface_get_capability_key_mgmt(res, strict, &capa, |
| buf, buflen); |
| |
| if (os_strcmp(field, "proto") == 0) |
| return ctrl_iface_get_capability_proto(res, strict, &capa, |
| buf, buflen); |
| |
| if (os_strcmp(field, "auth_alg") == 0) |
| return ctrl_iface_get_capability_auth_alg(res, strict, &capa, |
| buf, buflen); |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", |
| field); |
| |
| return -1; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, |
| const char *cmd, char *buf, |
| size_t buflen) |
| { |
| u8 bssid[ETH_ALEN]; |
| size_t i; |
| struct wpa_scan_results *results; |
| struct wpa_scan_res *bss; |
| int ret; |
| char *pos, *end; |
| const u8 *ie, *ie2; |
| |
| if (wpa_s->scan_res == NULL && |
| wpa_supplicant_get_scan_results(wpa_s) < 0) |
| return 0; |
| |
| results = wpa_s->scan_res; |
| if (results == NULL) |
| return 0; |
| |
| if (hwaddr_aton(cmd, bssid) == 0) { |
| for (i = 0; i < results->num; i++) { |
| if (os_memcmp(bssid, results->res[i]->bssid, ETH_ALEN) |
| == 0) |
| break; |
| } |
| } else |
| i = atoi(cmd); |
| |
| if (i >= results->num || results->res[i] == NULL) |
| return 0; /* no match found */ |
| |
| bss = results->res[i]; |
| pos = buf; |
| end = buf + buflen; |
| ret = os_snprintf(pos, end - pos, |
| "bssid=" MACSTR "\n" |
| "freq=%d\n" |
| "beacon_int=%d\n" |
| "capabilities=0x%04x\n" |
| "qual=%d\n" |
| "noise=%d\n" |
| "level=%d\n" |
| "tsf=%016llu\n" |
| "ie=", |
| MAC2STR(bss->bssid), bss->freq, bss->beacon_int, |
| bss->caps, bss->qual, bss->noise, bss->level, |
| (unsigned long long) bss->tsf); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ie = (const u8 *) (bss + 1); |
| for (i = 0; i < bss->ie_len; i++) { |
| ret = os_snprintf(pos, end - pos, "%02x", *ie++); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| ret = os_snprintf(pos, end - pos, "\n"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ret = os_snprintf(pos, end - pos, "flags="); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); |
| if (ie) |
| pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); |
| ie2 = wpa_scan_get_ie(bss, WLAN_EID_RSN); |
| if (ie2) |
| pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); |
| pos = wpa_supplicant_wps_ie_txt(pos, end, bss); |
| if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { |
| ret = os_snprintf(pos, end - pos, "[WEP]"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| if (bss->caps & IEEE80211_CAP_IBSS) { |
| ret = os_snprintf(pos, end - pos, "[IBSS]"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| } |
| |
| ret = os_snprintf(pos, end - pos, "\n"); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); |
| ret = os_snprintf(pos, end - pos, "ssid=%s\n", |
| ie ? wpa_ssid_txt(ie + 2, ie[1]) : ""); |
| if (ret < 0 || ret >= end - pos) |
| return pos - buf; |
| pos += ret; |
| |
| return pos - buf; |
| } |
| |
| |
| static int wpa_supplicant_ctrl_iface_ap_scan( |
| struct wpa_supplicant *wpa_s, char *cmd) |
| { |
| int ap_scan = atoi(cmd); |
| |
| if (ap_scan < 0 || ap_scan > 2) |
| return -1; |
| wpa_s->conf->ap_scan = ap_scan; |
| return 0; |
| } |
| |
| |
| char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, |
| char *buf, size_t *resp_len) |
| { |
| char *reply; |
| const int reply_size = 2048; |
| int ctrl_rsp = 0; |
| int reply_len; |
| |
| if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || |
| os_strncmp(buf, "SET_NETWORK ", 12) == 0) { |
| wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", |
| (const u8 *) buf, os_strlen(buf)); |
| } else { |
| wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", |
| (const u8 *) buf, os_strlen(buf)); |
| } |
| |
| reply = os_malloc(reply_size); |
| if (reply == NULL) { |
| *resp_len = 1; |
| return NULL; |
| } |
| |
| os_memcpy(reply, "OK\n", 3); |
| reply_len = 3; |
| |
| if (os_strcmp(buf, "PING") == 0) { |
| os_memcpy(reply, "PONG\n", 5); |
| reply_len = 5; |
| } else if (os_strcmp(buf, "MIB") == 0) { |
| reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); |
| if (reply_len >= 0) { |
| int res; |
| res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, |
| reply_size - reply_len); |
| if (res < 0) |
| reply_len = -1; |
| else |
| reply_len += res; |
| } |
| } else if (os_strncmp(buf, "STATUS", 6) == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_status( |
| wpa_s, buf + 6, reply, reply_size); |
| } else if (os_strcmp(buf, "PMKSA") == 0) { |
| reply_len = pmksa_cache_list(wpa_s->wpa, reply, reply_size); |
| } else if (os_strncmp(buf, "SET ", 4) == 0) { |
| if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) |
| reply_len = -1; |
| } else if (os_strcmp(buf, "LOGON") == 0) { |
| eapol_sm_notify_logoff(wpa_s->eapol, FALSE); |
| } else if (os_strcmp(buf, "LOGOFF") == 0) { |
| eapol_sm_notify_logoff(wpa_s->eapol, TRUE); |
| } else if (os_strcmp(buf, "REASSOCIATE") == 0) { |
| wpa_s->disconnected = 0; |
| wpa_s->reassociate = 1; |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| } else if (os_strcmp(buf, "RECONNECT") == 0) { |
| if (wpa_s->disconnected) { |
| wpa_s->disconnected = 0; |
| wpa_s->reassociate = 1; |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| } |
| #ifdef IEEE8021X_EAPOL |
| } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { |
| if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) |
| reply_len = -1; |
| #endif /* IEEE8021X_EAPOL */ |
| #ifdef CONFIG_PEERKEY |
| } else if (os_strncmp(buf, "STKSTART ", 9) == 0) { |
| if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9)) |
| reply_len = -1; |
| #endif /* CONFIG_PEERKEY */ |
| #ifdef CONFIG_IEEE80211R |
| } else if (os_strncmp(buf, "FT_DS ", 6) == 0) { |
| if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) |
| reply_len = -1; |
| #endif /* CONFIG_IEEE80211R */ |
| #ifdef CONFIG_WPS |
| } else if (os_strcmp(buf, "WPS_PBC") == 0) { |
| if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { |
| if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, |
| reply, |
| reply_size); |
| } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { |
| if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) |
| reply_len = -1; |
| #endif /* CONFIG_WPS */ |
| } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) |
| { |
| if (wpa_supplicant_ctrl_iface_ctrl_rsp( |
| wpa_s, buf + os_strlen(WPA_CTRL_RSP))) |
| reply_len = -1; |
| else |
| ctrl_rsp = 1; |
| } else if (os_strcmp(buf, "RECONFIGURE") == 0) { |
| if (wpa_supplicant_reload_configuration(wpa_s)) |
| reply_len = -1; |
| } else if (os_strcmp(buf, "TERMINATE") == 0) { |
| eloop_terminate(); |
| } else if (os_strncmp(buf, "BSSID ", 6) == 0) { |
| if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) |
| reply_len = -1; |
| } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_list_networks( |
| wpa_s, reply, reply_size); |
| } else if (os_strcmp(buf, "DISCONNECT") == 0) { |
| wpa_s->reassociate = 0; |
| wpa_s->disconnected = 1; |
| wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); |
| } else if (os_strcmp(buf, "SCAN") == 0) { |
| wpa_s->scan_req = 2; |
| wpa_supplicant_req_scan(wpa_s, 0, 0); |
| } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_scan_results( |
| wpa_s, reply, reply_size); |
| } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { |
| if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) { |
| if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) { |
| if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16)) |
| reply_len = -1; |
| } else if (os_strcmp(buf, "ADD_NETWORK") == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_add_network( |
| wpa_s, reply, reply_size); |
| } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) { |
| if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) { |
| if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_get_network( |
| wpa_s, buf + 12, reply, reply_size); |
| #ifndef CONFIG_NO_CONFIG_WRITE |
| } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { |
| if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) |
| reply_len = -1; |
| #endif /* CONFIG_NO_CONFIG_WRITE */ |
| } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_get_capability( |
| wpa_s, buf + 15, reply, reply_size); |
| } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { |
| if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) |
| reply_len = -1; |
| } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { |
| reply_len = wpa_supplicant_global_iface_list( |
| wpa_s->global, reply, reply_size); |
| } else if (os_strcmp(buf, "INTERFACES") == 0) { |
| reply_len = wpa_supplicant_global_iface_interfaces( |
| wpa_s->global, reply, reply_size); |
| } else if (os_strncmp(buf, "BSS ", 4) == 0) { |
| reply_len = wpa_supplicant_ctrl_iface_bss( |
| wpa_s, buf + 4, reply, reply_size); |
| } else { |
| os_memcpy(reply, "UNKNOWN COMMAND\n", 16); |
| reply_len = 16; |
| } |
| |
| if (reply_len < 0) { |
| os_memcpy(reply, "FAIL\n", 5); |
| reply_len = 5; |
| } |
| |
| if (ctrl_rsp) |
| eapol_sm_notify_ctrl_response(wpa_s->eapol); |
| |
| *resp_len = reply_len; |
| return reply; |
| } |
| |
| |
| static int wpa_supplicant_global_iface_add(struct wpa_global *global, |
| char *cmd) |
| { |
| struct wpa_interface iface; |
| char *pos; |
| |
| /* |
| * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param> |
| * TAB<bridge_ifname> |
| */ |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); |
| |
| os_memset(&iface, 0, sizeof(iface)); |
| |
| do { |
| iface.ifname = pos = cmd; |
| pos = os_strchr(pos, '\t'); |
| if (pos) |
| *pos++ = '\0'; |
| if (iface.ifname[0] == '\0') |
| return -1; |
| if (pos == NULL) |
| break; |
| |
| iface.confname = pos; |
| pos = os_strchr(pos, '\t'); |
| if (pos) |
| *pos++ = '\0'; |
| if (iface.confname[0] == '\0') |
| iface.confname = NULL; |
| if (pos == NULL) |
| break; |
| |
| iface.driver = pos; |
| pos = os_strchr(pos, '\t'); |
| if (pos) |
| *pos++ = '\0'; |
| if (iface.driver[0] == '\0') |
| iface.driver = NULL; |
| if (pos == NULL) |
| break; |
| |
| iface.ctrl_interface = pos; |
| pos = os_strchr(pos, '\t'); |
| if (pos) |
| *pos++ = '\0'; |
| if (iface.ctrl_interface[0] == '\0') |
| iface.ctrl_interface = NULL; |
| if (pos == NULL) |
| break; |
| |
| iface.driver_param = pos; |
| pos = os_strchr(pos, '\t'); |
| if (pos) |
| *pos++ = '\0'; |
| if (iface.driver_param[0] == '\0') |
| iface.driver_param = NULL; |
| if (pos == NULL) |
| break; |
| |
| iface.bridge_ifname = pos; |
| pos = os_strchr(pos, '\t'); |
| if (pos) |
| *pos++ = '\0'; |
| if (iface.bridge_ifname[0] == '\0') |
| iface.bridge_ifname = NULL; |
| if (pos == NULL) |
| break; |
| } while (0); |
| |
| if (wpa_supplicant_get_iface(global, iface.ifname)) |
| return -1; |
| |
| return wpa_supplicant_add_iface(global, &iface) ? 0 : -1; |
| } |
| |
| |
| static int wpa_supplicant_global_iface_remove(struct wpa_global *global, |
| char *cmd) |
| { |
| struct wpa_supplicant *wpa_s; |
| |
| wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); |
| |
| wpa_s = wpa_supplicant_get_iface(global, cmd); |
| if (wpa_s == NULL) |
| return -1; |
| return wpa_supplicant_remove_iface(global, wpa_s); |
| } |
| |
| |
| static void wpa_free_iface_info(struct wpa_interface_info *iface) |
| { |
| struct wpa_interface_info *prev; |
| |
| while (iface) { |
| prev = iface; |
| iface = iface->next; |
| |
| os_free(prev->ifname); |
| os_free(prev->desc); |
| os_free(prev); |
| } |
| } |
| |
| |
| static int wpa_supplicant_global_iface_list(struct wpa_global *global, |
| char *buf, int len) |
| { |
| int i, res; |
| struct wpa_interface_info *iface = NULL, *last = NULL, *tmp; |
| char *pos, *end; |
| |
| for (i = 0; wpa_supplicant_drivers[i]; i++) { |
| struct wpa_driver_ops *drv = wpa_supplicant_drivers[i]; |
| if (drv->get_interfaces == NULL) |
| continue; |
| tmp = drv->get_interfaces(global->drv_priv); |
| if (tmp == NULL) |
| continue; |
| |
| if (last == NULL) |
| iface = last = tmp; |
| else |
| last->next = tmp; |
| while (last->next) |
| last = last->next; |
| } |
| |
| pos = buf; |
| end = buf + len; |
| for (tmp = iface; tmp; tmp = tmp->next) { |
| res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", |
| tmp->drv_name, tmp->ifname, |
| tmp->desc ? tmp->desc : ""); |
| if (res < 0 || res >= end - pos) { |
| *pos = '\0'; |
| break; |
| } |
| pos += res; |
| } |
| |
| wpa_free_iface_info(iface); |
| |
| return pos - buf; |
| } |
| |
| |
| static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, |
| char *buf, int len) |
| { |
| int res; |
| char *pos, *end; |
| struct wpa_supplicant *wpa_s; |
| |
| wpa_s = global->ifaces; |
| pos = buf; |
| end = buf + len; |
| |
| while (wpa_s) { |
| res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); |
| if (res < 0 || res >= end - pos) { |
| *pos = '\0'; |
| break; |
| } |
| pos += res; |
| wpa_s = wpa_s->next; |
| } |
| return pos - buf; |
| } |
| |
| |
| char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, |
| char *buf, size_t *resp_len) |
| { |
| char *reply; |
| const int reply_size = 2048; |
| int reply_len; |
| |
| wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface", |
| (const u8 *) buf, os_strlen(buf)); |
| |
| reply = os_malloc(reply_size); |
| if (reply == NULL) { |
| *resp_len = 1; |
| return NULL; |
| } |
| |
| os_memcpy(reply, "OK\n", 3); |
| reply_len = 3; |
| |
| if (os_strcmp(buf, "PING") == 0) { |
| os_memcpy(reply, "PONG\n", 5); |
| reply_len = 5; |
| } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) { |
| if (wpa_supplicant_global_iface_add(global, buf + 14)) |
| reply_len = -1; |
| } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { |
| if (wpa_supplicant_global_iface_remove(global, buf + 17)) |
| reply_len = -1; |
| } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { |
| reply_len = wpa_supplicant_global_iface_list( |
| global, reply, reply_size); |
| } else if (os_strcmp(buf, "INTERFACES") == 0) { |
| reply_len = wpa_supplicant_global_iface_interfaces( |
| global, reply, reply_size); |
| } else if (os_strcmp(buf, "TERMINATE") == 0) { |
| eloop_terminate(); |
| } else { |
| os_memcpy(reply, "UNKNOWN COMMAND\n", 16); |
| reply_len = 16; |
| } |
| |
| if (reply_len < 0) { |
| os_memcpy(reply, "FAIL\n", 5); |
| reply_len = 5; |
| } |
| |
| *resp_len = reply_len; |
| return reply; |
| } |