/* * hidl interface for wpa_supplicant daemon * Copyright (c) 2004-2016, Jouni Malinen * Copyright (c) 2004-2016, Roshan Pius * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "hidl_manager.h" #include "hidl_return_util.h" #include "supplicant.h" #include #include #include namespace { using namespace android::hardware::wifi::supplicant::V1_2; // 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 kP2pIfaceConfPath[] = "/data/vendor/wifi/wpa/p2p_supplicant.conf"; constexpr char kP2pIfaceConfOverlayPath[] = "/vendor/etc/wifi/p2p_supplicant_overlay.conf"; // Migrate conf files for existing devices. constexpr char kSystemTemplateConfPath[] = "/system/etc/wifi/wpa_supplicant.conf"; constexpr char kVendorTemplateConfPath[] = "/vendor/etc/wifi/wpa_supplicant.conf"; constexpr char kOldStaIfaceConfPath[] = "/data/misc/wifi/wpa_supplicant.conf"; constexpr char kOldP2pIfaceConfPath[] = "/data/misc/wifi/p2p_supplicant.conf"; constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; 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; } ret = copyFileIfItExists(kVendorTemplateConfPath, config_file_path); if (ret == 0) { wpa_printf( MSG_INFO, "Copied template conf file from %s to %s", kVendorTemplateConfPath, config_file_path.c_str()); return 0; } else if (ret == -1) { unlink(config_file_path.c_str()); return -1; } ret = copyFileIfItExists(kSystemTemplateConfPath, config_file_path); if (ret == 0) { wpa_printf( MSG_INFO, "Copied template conf file from %s to %s", kSystemTemplateConfPath, 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 android { namespace hardware { namespace wifi { namespace supplicant { namespace V1_2 { namespace implementation { using hidl_return_util::validateAndCall; Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {} bool Supplicant::isValid() { // This top level object cannot be invalidated. return true; } Return Supplicant::addInterface( const IfaceInfo& iface_info, addInterface_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::addInterfaceInternal, _hidl_cb, iface_info); } Return Supplicant::removeInterface( const IfaceInfo& iface_info, removeInterface_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::removeInterfaceInternal, _hidl_cb, iface_info); } Return Supplicant::getInterface( const IfaceInfo& iface_info, getInterface_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::getInterfaceInternal, _hidl_cb, iface_info); } Return Supplicant::listInterfaces(listInterfaces_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::listInterfacesInternal, _hidl_cb); } Return Supplicant::registerCallback( const sp& callback, registerCallback_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::registerCallbackInternal, _hidl_cb, callback); } Return Supplicant::setDebugParams( ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys, setDebugParams_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::setDebugParamsInternal, _hidl_cb, level, show_timestamp, show_keys); } Return Supplicant::setConcurrencyPriority( IfaceType type, setConcurrencyPriority_cb _hidl_cb) { return validateAndCall( this, SupplicantStatusCode::FAILURE_IFACE_INVALID, &Supplicant::setConcurrencyPriorityInternal, _hidl_cb, type); } Return Supplicant::getDebugLevel() { // TODO: Add SupplicantStatus in this method return for uniformity with // the other methods in supplicant HIDL interface. return (ISupplicant::DebugLevel)wpa_debug_level; } Return Supplicant::isDebugShowTimestampEnabled() { // TODO: Add SupplicantStatus in this method return for uniformity with // the other methods in supplicant HIDL interface. return ((wpa_debug_timestamp != 0) ? true : false); } Return Supplicant::isDebugShowKeysEnabled() { // TODO: Add SupplicantStatus in this method return for uniformity with // the other methods in supplicant HIDL interface. return ((wpa_debug_show_keys != 0) ? true : false); } Return Supplicant::terminate() { wpa_printf(MSG_INFO, "Terminating..."); wpa_supplicant_terminate_proc(wpa_global_); return Void(); } std::pair> Supplicant::addInterfaceInternal(const IfaceInfo& iface_info) { android::sp iface; // Check if required |ifname| argument is empty. if (iface_info.name.empty()) { return {{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. SupplicantStatus status; std::tie(status, iface) = getInterfaceInternal(iface_info); if (status.code == SupplicantStatusCode::SUCCESS) { return {{SupplicantStatusCode::FAILURE_IFACE_EXISTS, ""}, iface}; } struct wpa_interface iface_params = {}; iface_params.driver = kIfaceDriverName; if (iface_info.type == IfaceType::P2P) { if (ensureConfigFileExists( kP2pIfaceConfPath, kOldP2pIfaceConfPath) != 0) { wpa_printf( MSG_ERROR, "Conf file does not exists: %s", kP2pIfaceConfPath); return {{SupplicantStatusCode::FAILURE_UNKNOWN, "Conf file does not exist"}, {}}; } iface_params.confname = kP2pIfaceConfPath; int ret = access(kP2pIfaceConfOverlayPath, R_OK); if (ret == 0) { iface_params.confanother = kP2pIfaceConfOverlayPath; } } else { if (ensureConfigFileExists( kStaIfaceConfPath, kOldStaIfaceConfPath) != 0) { wpa_printf( MSG_ERROR, "Conf file does not exists: %s", kStaIfaceConfPath); return {{SupplicantStatusCode::FAILURE_UNKNOWN, "Conf file does not exist"}, {}}; } iface_params.confname = kStaIfaceConfPath; int ret = access(kStaIfaceConfOverlayPath, R_OK); if (ret == 0) { iface_params.confanother = kStaIfaceConfOverlayPath; } } iface_params.ifname = iface_info.name.c_str(); struct wpa_supplicant* wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL); if (!wpa_s) { return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}}; } // The supplicant core creates a corresponding hidl object via // HidlManager when |wpa_supplicant_add_iface| is called. return getInterfaceInternal(iface_info); } SupplicantStatus 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 {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""}; } if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) { return {SupplicantStatusCode::FAILURE_UNKNOWN, ""}; } return {SupplicantStatusCode::SUCCESS, ""}; } std::pair> Supplicant::getInterfaceInternal(const IfaceInfo& iface_info) { struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str()); if (!wpa_s) { return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""}, nullptr}; } HidlManager* hidl_manager = HidlManager::getInstance(); if (iface_info.type == IfaceType::P2P) { android::sp iface; if (!hidl_manager || hidl_manager->getP2pIfaceHidlObjectByIfname( wpa_s->ifname, &iface)) { return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, iface}; } // Set this flag true here, since there is no HIDL 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 {{SupplicantStatusCode::SUCCESS, ""}, iface}; } else { android::sp iface; if (!hidl_manager || hidl_manager->getStaIfaceHidlObjectByIfname( wpa_s->ifname, &iface)) { return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, iface}; } return {{SupplicantStatusCode::SUCCESS, ""}, iface}; } } std::pair> 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(ISupplicant::IfaceInfo{ IfaceType::P2P, wpa_s->ifname}); } else { ifaces.emplace_back(ISupplicant::IfaceInfo{ IfaceType::STA, wpa_s->ifname}); } } return {{SupplicantStatusCode::SUCCESS, ""}, std::move(ifaces)}; } SupplicantStatus Supplicant::registerCallbackInternal( const sp& callback) { HidlManager* hidl_manager = HidlManager::getInstance(); if (!hidl_manager || hidl_manager->addSupplicantCallbackHidlObject(callback)) { return {SupplicantStatusCode::FAILURE_UNKNOWN, ""}; } return {SupplicantStatusCode::SUCCESS, ""}; } SupplicantStatus Supplicant::setDebugParamsInternal( ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys) { if (wpa_supplicant_set_debug_params( wpa_global_, static_cast(level), show_timestamp, show_keys)) { return {SupplicantStatusCode::FAILURE_UNKNOWN, ""}; } return {SupplicantStatusCode::SUCCESS, ""}; } SupplicantStatus 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 {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""}; } return SupplicantStatus{SupplicantStatusCode::SUCCESS, ""}; } } // namespace implementation } // namespace V1_2 } // namespace supplicant } // namespace wifi } // namespace hardware } // namespace android