/* * WPA Supplicant - Supplicant Aidl interface * Copyright (c) 2021, Google Inc. All rights reserved. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "aidl_manager.h" #include "aidl_return_util.h" #include "misc_utils.h" #include "supplicant.h" #include "p2p_iface.h" #include #include #include namespace { // Pre-populated interface params for interfaces controlled by wpa_supplicant. // Note: This may differ for other OEM's. So, modify this accordingly. constexpr char kIfaceDriverName[] = "nl80211"; constexpr char kStaIfaceConfPath[] = "/data/vendor/wifi/wpa/wpa_supplicant.conf"; constexpr char kStaIfaceConfOverlayPath[] = "/vendor/etc/wifi/wpa_supplicant_overlay.conf"; constexpr char kRtkStaIfaceConfOverlayPath[] = "/vendor/etc/wifi/wpa_supplicant_rtk.conf"; constexpr char kP2pIfaceConfPath[] = "/data/vendor/wifi/wpa/p2p_supplicant.conf"; constexpr char kP2pIfaceConfOverlayPath[] = "/vendor/etc/wifi/p2p_supplicant_overlay.conf"; constexpr char kRtkP2pIfaceConfOverlayPath[] = "/vendor/etc/wifi/p2p_supplicant_rtk.conf"; // Migrate conf files for existing devices. static const char* kTemplateConfPaths[] = { "/apex/com.android.wifi.hal/etc/wifi/wpa_supplicant.conf", "/vendor/etc/wifi/wpa_supplicant.conf", "/system/etc/wifi/wpa_supplicant.conf", }; static char wpa_conf_overlay_path[64]; static char p2p_conf_overlay_path[64]; constexpr char kOldStaIfaceConfPath[] = "/vendor/etc/wifi/wpa_supplicant.conf"; constexpr char kOldP2pIfaceConfPath[] = "/vendor/etc/wifi/p2p_supplicant.conf"; constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; const char* resolvePath(const char* paths[], size_t size) { for (int i = 0; i < size; ++i) { if (access(paths[i], R_OK) == 0) { return paths[i]; } } return nullptr; } int copyFile( const std::string& src_file_path, const std::string& dest_file_path) { std::string file_contents; if (!android::base::ReadFileToString(src_file_path, &file_contents)) { wpa_printf( MSG_ERROR, "Failed to read from %s. Errno: %s", src_file_path.c_str(), strerror(errno)); return -1; } if (!android::base::WriteStringToFile( file_contents, dest_file_path, kConfigFileMode, getuid(), getgid())) { wpa_printf( MSG_ERROR, "Failed to write to %s. Errno: %s", dest_file_path.c_str(), strerror(errno)); return -1; } return 0; } /** * Copy |src_file_path| to |dest_file_path| if it exists. * * Returns 1 if |src_file_path| does not exist or not accessible, * Returns -1 if the copy fails. * Returns 0 if the copy succeeds. */ int copyFileIfItExists( const std::string& src_file_path, const std::string& dest_file_path) { int ret = access(src_file_path.c_str(), R_OK); // Sepolicy denial (2018+ device) will return EACCESS instead of ENOENT. if ((ret != 0) && ((errno == ENOENT) || (errno == EACCES))) { return 1; } ret = copyFile(src_file_path, dest_file_path); if (ret != 0) { wpa_printf( MSG_ERROR, "Failed copying %s to %s.", src_file_path.c_str(), dest_file_path.c_str()); return -1; } return 0; } /** * Ensure that the specified config file pointed by |config_file_path| exists. * a) If the |config_file_path| exists with the correct permissions, return. * b) If the |config_file_path| does not exist, but |old_config_file_path| * exists, copy over the contents of the |old_config_file_path| to * |config_file_path|. * c) If the |config_file_path| & |old_config_file_path| * does not exists, copy over the contents of |template_config_file_path|. */ int ensureConfigFileExists( const std::string& config_file_path, const std::string& old_config_file_path) { int ret = access(config_file_path.c_str(), R_OK | W_OK); if (ret == 0) { return 0; } if (errno == EACCES) { ret = chmod(config_file_path.c_str(), kConfigFileMode); if (ret == 0) { return 0; } else { wpa_printf( MSG_ERROR, "Cannot set RW to %s. Errno: %s", config_file_path.c_str(), strerror(errno)); return -1; } } else if (errno != ENOENT) { wpa_printf( MSG_ERROR, "Cannot acces %s. Errno: %s", config_file_path.c_str(), strerror(errno)); return -1; } ret = copyFileIfItExists(old_config_file_path, config_file_path); if (ret == 0) { wpa_printf( MSG_INFO, "Migrated conf file from %s to %s", old_config_file_path.c_str(), config_file_path.c_str()); unlink(old_config_file_path.c_str()); return 0; } else if (ret == -1) { unlink(config_file_path.c_str()); return -1; } const char* path = resolvePath(kTemplateConfPaths, sizeof(kTemplateConfPaths)/sizeof(kTemplateConfPaths[0])); if (path != nullptr) { ret = copyFileIfItExists(path, config_file_path); if (ret == 0) { wpa_printf( MSG_INFO, "Copied template conf file from %s to %s", path, config_file_path.c_str()); return 0; } else if (ret == -1) { unlink(config_file_path.c_str()); return -1; } } // Did not create the conf file. return -1; } } // namespace namespace aidl { namespace android { namespace hardware { namespace wifi { namespace supplicant { using aidl_return_util::validateAndCall; using misc_utils::createStatus; using misc_utils::createStatusWithMsg; Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {} bool Supplicant::isValid() { // This top level object cannot be invalidated. return true; } ::ndk::ScopedAStatus Supplicant::addP2pInterface( const std::string& in_name, std::shared_ptr* _aidl_return) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::addP2pInterfaceInternal, _aidl_return, in_name); } ::ndk::ScopedAStatus Supplicant::addStaInterface( const std::string& in_name, std::shared_ptr* _aidl_return) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::addStaInterfaceInternal, _aidl_return, in_name); } ::ndk::ScopedAStatus Supplicant::removeInterface( const IfaceInfo& in_ifaceInfo) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::removeInterfaceInternal, in_ifaceInfo); } ::ndk::ScopedAStatus Supplicant::getP2pInterface( const std::string& in_name, std::shared_ptr* _aidl_return) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::getP2pInterfaceInternal, _aidl_return, in_name); } ::ndk::ScopedAStatus Supplicant::getStaInterface( const std::string& in_name, std::shared_ptr* _aidl_return) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::getStaInterfaceInternal, _aidl_return, in_name); } ::ndk::ScopedAStatus Supplicant::listInterfaces( std::vector* _aidl_return) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::listInterfacesInternal, _aidl_return); } ::ndk::ScopedAStatus Supplicant::registerCallback( const std::shared_ptr& in_callback) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::registerCallbackInternal, in_callback); } ::ndk::ScopedAStatus Supplicant::setDebugParams( DebugLevel in_level, bool in_showTimestamp, bool in_showKeys) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::setDebugParamsInternal, in_level, in_showTimestamp, in_showKeys); } ::ndk::ScopedAStatus Supplicant::setConcurrencyPriority( IfaceType in_type) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::setConcurrencyPriorityInternal, in_type); } ::ndk::ScopedAStatus Supplicant::getDebugLevel(DebugLevel* _aidl_return) { *_aidl_return = static_cast(wpa_debug_level); return ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus Supplicant::isDebugShowTimestampEnabled(bool* _aidl_return) { *_aidl_return = ((wpa_debug_timestamp != 0) ? true : false); return ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus Supplicant::isDebugShowKeysEnabled(bool* _aidl_return) { *_aidl_return = ((wpa_debug_show_keys != 0) ? true : false); return ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus Supplicant::terminate() { wpa_printf(MSG_INFO, "Terminating..."); wpa_supplicant_terminate_proc(wpa_global_); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Supplicant::addP2pDevInterface(struct wpa_interface iface_params) { char primary_ifname[IFNAMSIZ]; u32 primary_ifname_len = strlen(iface_params.ifname) - strlen(P2P_MGMT_DEVICE_PREFIX); if(primary_ifname_len > IFNAMSIZ) { wpa_printf(MSG_DEBUG, "%s, Invalid primary iface name ", __FUNCTION__); return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID); } strncpy(primary_ifname, iface_params.ifname + strlen(P2P_MGMT_DEVICE_PREFIX), primary_ifname_len); wpa_printf(MSG_DEBUG, "%s, Initialize p2p-dev-wlan0 iface with" "primary_iface = %s", __FUNCTION__, primary_ifname); struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, primary_ifname); if (!wpa_s) { wpa_printf(MSG_DEBUG, "%s,NULL wpa_s for wlan0", __FUNCTION__); return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN); } const u8 *if_addr = NULL; char force_name[100] = {'\0'}; wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE; if (wpa_s->conf->p2p_device_random_mac_addr == 2 && !is_zero_ether_addr(wpa_s->conf->p2p_device_persistent_mac_addr)) if_addr = wpa_s->conf->p2p_device_persistent_mac_addr; int ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, iface_params.ifname, if_addr, NULL, force_name, wpa_s->pending_interface_addr, NULL); if (ret < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface"); return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN); } os_strlcpy(wpa_s->pending_interface_name, iface_params.ifname, sizeof(wpa_s->pending_interface_name)); iface_params.p2p_mgmt = 1; iface_params.driver_param = wpa_s->conf->driver_param; iface_params.ctrl_interface = NULL; struct wpa_supplicant *p2pdev_wpa_s = wpa_supplicant_add_iface( wpa_s->global, &iface_params, wpa_s); if (!p2pdev_wpa_s) { wpa_printf(MSG_INFO, "Failed to enable P2P Device"); return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN); } p2pdev_wpa_s->p2pdev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return ndk::ScopedAStatus::ok(); } std::pair, ndk::ScopedAStatus> Supplicant::addP2pInterfaceInternal(const std::string& name) { std::shared_ptr iface; // Check if required |ifname| argument is empty. if (name.empty()) { return {nullptr, createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID)}; } if (strncmp(name.c_str(), P2P_MGMT_DEVICE_PREFIX, strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, name.c_str()); if (wpa_s) { wpa_printf(MSG_DEBUG, "Remove existing p2p dev interface"); wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0); } } // Try to get the wpa_supplicant record for this iface, return // the iface object with the appropriate status code if it exists. ndk::ScopedAStatus status; std::tie(iface, status) = getP2pInterfaceInternal(name); if (status.isOk()) { wpa_printf(MSG_INFO, "Iface already exists, return existing"); return {iface, ndk::ScopedAStatus::ok()}; } struct wpa_interface iface_params = {}; iface_params.driver = kIfaceDriverName; if (ensureConfigFileExists( kP2pIfaceConfPath, kOldP2pIfaceConfPath) != 0) { wpa_printf( MSG_ERROR, "Conf file does not exists: %s", kP2pIfaceConfPath); return {nullptr, createStatusWithMsg( SupplicantStatusCode::FAILURE_UNKNOWN, "Conf file does not exist")}; } iface_params.confname = kP2pIfaceConfPath; if (wifi_type[0] == 0) { check_wifi_chip_type_string(wifi_type); } if (0 == strncmp(wifi_type, "RTL", 3)) { strcpy(p2p_conf_overlay_path, kRtkP2pIfaceConfOverlayPath); } else { strcpy(p2p_conf_overlay_path, kP2pIfaceConfOverlayPath); } int ret = access(p2p_conf_overlay_path, R_OK); wpa_printf(MSG_INFO, "p2p_overlay: %s, read %s.\n", p2p_conf_overlay_path, (ret == 0) ? "ok" : "fail"); if (ret == 0) { iface_params.confanother = p2p_conf_overlay_path; } iface_params.ifname = name.c_str(); if (strncmp(iface_params.ifname, P2P_MGMT_DEVICE_PREFIX, strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { status = addP2pDevInterface(iface_params); if (!status.isOk()) { return {iface, createStatus(static_cast( status.getServiceSpecificError()))}; } } else { struct wpa_supplicant* wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL); if (!wpa_s) { return {nullptr, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)}; } // Request the current scan results from the driver and update // the local BSS list wpa_s->bss. This is to avoid a full scan // while processing the connect request on newly created interface. wpa_supplicant_update_scan_results(wpa_s); } // The supplicant core creates a corresponding aidl object via // AidlManager when |wpa_supplicant_add_iface| is called. return getP2pInterfaceInternal(name); } std::pair, ndk::ScopedAStatus> Supplicant::addStaInterfaceInternal(const std::string& name) { std::shared_ptr iface; // Check if required |ifname| argument is empty. if (name.empty()) { return {nullptr, createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID)}; } // Try to get the wpa_supplicant record for this iface, return // the iface object with the appropriate status code if it exists. ndk::ScopedAStatus status; std::tie(iface, status) = getStaInterfaceInternal(name); if (status.isOk()) { wpa_printf(MSG_INFO, "Iface already exists, return existing"); return {iface, ndk::ScopedAStatus::ok()}; } struct wpa_interface iface_params = {}; iface_params.driver = kIfaceDriverName; if (ensureConfigFileExists( kStaIfaceConfPath, kOldStaIfaceConfPath) != 0) { wpa_printf( MSG_ERROR, "Conf file does not exists: %s", kStaIfaceConfPath); return {nullptr, createStatusWithMsg( SupplicantStatusCode::FAILURE_UNKNOWN, "Conf file does not exist")}; } iface_params.confname = kStaIfaceConfPath; if (wifi_type[0] == 0) { check_wifi_chip_type_string(wifi_type); } if (0 == strncmp(wifi_type, "RTL", 3)) { strcpy(wpa_conf_overlay_path, kRtkStaIfaceConfOverlayPath); } else { strcpy(wpa_conf_overlay_path, kStaIfaceConfOverlayPath); } int ret = access(wpa_conf_overlay_path, R_OK); wpa_printf(MSG_INFO, "wpa_overlay: %s, read %s.\n", wpa_conf_overlay_path, (ret == 0) ? "ok" : "fail"); if (ret == 0) { iface_params.confanother = wpa_conf_overlay_path; } iface_params.ifname = name.c_str(); if (strncmp(iface_params.ifname, P2P_MGMT_DEVICE_PREFIX, strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { status = addP2pDevInterface(iface_params); if (!status.isOk()) { return {iface, createStatus(static_cast( status.getServiceSpecificError()))}; } } else { struct wpa_supplicant* wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL); if (!wpa_s) { return {nullptr, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)}; } // Request the current scan results from the driver and update // the local BSS list wpa_s->bss. This is to avoid a full scan // while processing the connect request on newly created interface. wpa_supplicant_update_scan_results(wpa_s); } // The supplicant core creates a corresponding aidl object via // AidlManager when |wpa_supplicant_add_iface| is called. return getStaInterfaceInternal(name); } ndk::ScopedAStatus Supplicant::removeInterfaceInternal( const IfaceInfo& iface_info) { struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str()); if (!wpa_s) { return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN); } if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) { return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN); } return ndk::ScopedAStatus::ok(); } std::pair, ndk::ScopedAStatus> Supplicant::getP2pInterfaceInternal(const std::string& name) { struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, name.c_str()); if (!wpa_s) { return {nullptr, createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN)}; } AidlManager* aidl_manager = AidlManager::getInstance(); std::shared_ptr iface; if (!aidl_manager || aidl_manager->getP2pIfaceAidlObjectByIfname( wpa_s->ifname, &iface)) { return {iface, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)}; } // Set this flag true here, since there is no AIDL initialize // method for the p2p config, and the supplicant interface is // not ready when the p2p iface is created. wpa_s->conf->persistent_reconnect = true; return {iface, ndk::ScopedAStatus::ok()}; } std::pair, ndk::ScopedAStatus> Supplicant::getStaInterfaceInternal(const std::string& name) { struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, name.c_str()); if (!wpa_s) { return {nullptr, createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN)}; } AidlManager* aidl_manager = AidlManager::getInstance(); std::shared_ptr iface; if (!aidl_manager || aidl_manager->getStaIfaceAidlObjectByIfname( wpa_s->ifname, &iface)) { return {iface, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)}; } return {iface, ndk::ScopedAStatus::ok()}; } std::pair, ndk::ScopedAStatus> Supplicant::listInterfacesInternal() { std::vector ifaces; for (struct wpa_supplicant* wpa_s = wpa_global_->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_s->global->p2p_init_wpa_s == wpa_s) { ifaces.emplace_back(IfaceInfo{ IfaceType::P2P, wpa_s->ifname}); } else { ifaces.emplace_back(IfaceInfo{ IfaceType::STA, wpa_s->ifname}); } } return {std::move(ifaces), ndk::ScopedAStatus::ok()}; } ndk::ScopedAStatus Supplicant::registerCallbackInternal( const std::shared_ptr& callback) { AidlManager* aidl_manager = AidlManager::getInstance(); if (!aidl_manager || aidl_manager->addSupplicantCallbackAidlObject(callback)) { return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Supplicant::setDebugParamsInternal( DebugLevel level, bool show_timestamp, bool show_keys) { if (wpa_supplicant_set_debug_params( wpa_global_, static_cast(level), show_timestamp, show_keys)) { return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Supplicant::setConcurrencyPriorityInternal(IfaceType type) { if (type == IfaceType::STA) { wpa_global_->conc_pref = wpa_global::wpa_conc_pref::WPA_CONC_PREF_STA; } else if (type == IfaceType::P2P) { wpa_global_->conc_pref = wpa_global::wpa_conc_pref::WPA_CONC_PREF_P2P; } else { return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID); } return ndk::ScopedAStatus::ok(); } } // namespace supplicant } // namespace wifi } // namespace hardware } // namespace android } // namespace aidl