/* * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include <dbus/dbus.h> #include "common.h" #include "eap_peer/eap_methods.h" #include "common/ieee802_11_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "../config.h" #include "../wpa_supplicant_i.h" #include "../driver_i.h" #include "../notify.h" #include "../wpas_glue.h" #include "../bss.h" #include "../scan.h" #include "dbus_old.h" #include "dbus_old_handlers.h" #include "dbus_dict_helpers.h" extern int wpa_debug_level; extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; /** * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message * @message: Pointer to incoming dbus message this error refers to * Returns: a dbus error message * * Convenience function to create and return an invalid options error */ DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, const char *arg) { DBusMessage *reply; reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS, "Did not receive correct message " "arguments."); if (arg != NULL) dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); return reply; } /** * wpas_dbus_new_success_reply - Return a new success reply message * @message: Pointer to incoming dbus message this reply refers to * Returns: a dbus message containing a single UINT32 that indicates * success (ie, a value of 1) * * Convenience function to create and return a success reply message */ DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message) { DBusMessage *reply; unsigned int success = 1; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success, DBUS_TYPE_INVALID); return reply; } /** * wpas_dbus_global_add_interface - Request registration of a network interface * @message: Pointer to incoming dbus message * @global: %wpa_supplicant global data structure * Returns: The object path of the new interface object, * or a dbus error message with more information * * Handler function for "addInterface" method call. Handles requests * by dbus clients to register a network interface that wpa_supplicant * will manage. */ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, struct wpa_global *global) { char *ifname = NULL; char *driver = NULL; char *driver_param = NULL; char *confname = NULL; char *bridge_ifname = NULL; DBusMessage *reply = NULL; DBusMessageIter iter; dbus_message_iter_init(message, &iter); /* First argument: interface name (DBUS_TYPE_STRING) * Required; must be non-zero length */ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) goto error; dbus_message_iter_get_basic(&iter, &ifname); if (!os_strlen(ifname)) goto error; /* Second argument: dict of options */ if (dbus_message_iter_next(&iter)) { DBusMessageIter iter_dict; struct wpa_dbus_dict_entry entry; if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (!strcmp(entry.key, "driver") && (entry.type == DBUS_TYPE_STRING)) { driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) goto error; } else if (!strcmp(entry.key, "driver-params") && (entry.type == DBUS_TYPE_STRING)) { driver_param = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver_param == NULL) goto error; } else if (!strcmp(entry.key, "config-file") && (entry.type == DBUS_TYPE_STRING)) { confname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (confname == NULL) goto error; } else if (!strcmp(entry.key, "bridge-ifname") && (entry.type == DBUS_TYPE_STRING)) { bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (bridge_ifname == NULL) goto error; } else { wpa_dbus_dict_entry_clear(&entry); goto error; } } } /* * Try to get the wpa_supplicant record for this iface, return * an error if we already control it. */ if (wpa_supplicant_get_iface(global, ifname) != NULL) { reply = dbus_message_new_error(message, WPAS_ERROR_EXISTS_ERROR, "wpa_supplicant already " "controls this interface."); } else { struct wpa_supplicant *wpa_s; struct wpa_interface iface; os_memset(&iface, 0, sizeof(iface)); iface.ifname = ifname; iface.driver = driver; iface.driver_param = driver_param; iface.confname = confname; iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { const char *path = wpa_s->dbus_path; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } else { reply = dbus_message_new_error(message, WPAS_ERROR_ADD_ERROR, "wpa_supplicant " "couldn't grab this " "interface."); } } out: os_free(driver); os_free(driver_param); os_free(confname); os_free(bridge_ifname); return reply; error: reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } /** * wpas_dbus_global_remove_interface - Request deregistration of an interface * @message: Pointer to incoming dbus message * @global: wpa_supplicant global data structure * Returns: a dbus message containing a UINT32 indicating success (1) or * failure (0), or returns a dbus error message with more information * * Handler function for "removeInterface" method call. Handles requests * by dbus clients to deregister a network interface that wpa_supplicant * currently manages. */ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, struct wpa_global *global) { struct wpa_supplicant *wpa_s; char *path; DBusMessage *reply = NULL; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path); if (wpa_s == NULL) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_new_success_reply(message); } else { reply = dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, "wpa_supplicant couldn't " "remove this interface."); } out: return reply; } /** * wpas_dbus_global_get_interface - Get the object path for an interface name * @message: Pointer to incoming dbus message * @global: %wpa_supplicant global data structure * Returns: The object path of the interface object, * or a dbus error message with more information * * Handler function for "getInterface" method call. Handles requests * by dbus clients for the object path of an specific network interface. */ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, struct wpa_global *global) { DBusMessage *reply = NULL; const char *ifname; const char *path; struct wpa_supplicant *wpa_s; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &ifname, DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } wpa_s = wpa_supplicant_get_iface(global, ifname); if (wpa_s == NULL) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } path = wpa_s->dbus_path; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); out: return reply; } /** * wpas_dbus_global_set_debugparams- Set the debug params * @message: Pointer to incoming dbus message * @global: %wpa_supplicant global data structure * Returns: a dbus message containing a UINT32 indicating success (1) or * failure (0), or returns a dbus error message with more information * * Handler function for "setDebugParams" method call. Handles requests * by dbus clients for the object path of an specific network interface. */ DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, struct wpa_global *global) { DBusMessage *reply = NULL; int debug_level; dbus_bool_t debug_timestamp; dbus_bool_t debug_show_keys; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &debug_level, DBUS_TYPE_BOOLEAN, &debug_timestamp, DBUS_TYPE_BOOLEAN, &debug_show_keys, DBUS_TYPE_INVALID)) { return wpas_dbus_new_invalid_opts_error(message, NULL); } if (wpa_supplicant_set_debug_params(global, debug_level, debug_timestamp ? 1 : 0, debug_show_keys ? 1 : 0)) { return wpas_dbus_new_invalid_opts_error(message, NULL); } reply = wpas_dbus_new_success_reply(message); return reply; } /** * wpas_dbus_iface_scan - Request a wireless scan on an interface * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: a dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "scan" method call of a network device. Requests * that wpa_supplicant perform a wireless scan as soon as possible * on a particular wireless interface. */ DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, struct wpa_supplicant *wpa_s) { wpa_s->scan_req = 2; wpa_supplicant_req_scan(wpa_s, 0, 0); return wpas_dbus_new_success_reply(message); } /** * wpas_dbus_iface_scan_results - Get the results of a recent scan request * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: a dbus message containing a dbus array of objects paths, or returns * a dbus error message if not scan results could be found * * Handler function for "scanResults" method call of a network device. Returns * a dbus message containing the object paths of wireless networks found. */ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; DBusMessageIter iter; DBusMessageIter sub_iter; struct wpa_bss *bss; /* Create and initialize the return message */ reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &sub_iter); /* Loop through scan results and append each result's object path */ dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; char *path = path_buf; /* Construct the object path for this network. Note that ':' * is not a valid character in dbus object paths. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_BSSIDS_PART "/" WPAS_DBUS_BSSID_FORMAT, wpa_s->dbus_path, MAC2STR(bss->bssid)); dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_OBJECT_PATH, &path); } dbus_message_iter_close_container(&iter, &sub_iter); return reply; } /** * wpas_dbus_bssid_properties - Return the properties of a scanned network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * @res: wpa_supplicant scan result for which to get properties * Returns: a dbus message containing the properties for the requested network * * Handler function for "properties" method call of a scanned network. * Returns a dbus message containing the the properties. */ DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { DBusMessage *reply; DBusMessageIter iter, iter_dict; const u8 *ie; /* Dump the properties into a dbus message */ reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) goto error; if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", (const char *) bss->bssid, ETH_ALEN)) goto error; ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", (const char *) (ie + 2), ie[1])) goto error; } ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", (const char *) ie, ie[1] + 2)) goto error; } ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", (const char *) ie, ie[1] + 2)) goto error; } ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", (const char *) ie, ie[1] + 2)) goto error; } if (bss->freq) { if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) goto error; } if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", bss->caps)) goto error; if (!(bss->flags & WPA_BSS_QUAL_INVALID) && !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) goto error; if (!(bss->flags & WPA_BSS_NOISE_INVALID) && !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) goto error; if (!(bss->flags & WPA_BSS_LEVEL_INVALID) && !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) goto error; if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", wpa_bss_get_max_rate(bss) * 500000)) goto error; if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) goto error; return reply; error: if (reply) dbus_message_unref(reply); return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, "an internal error occurred returning " "BSSID properties."); } /** * wpas_dbus_iface_capabilities - Return interface capabilities * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a dict of strings * * Handler function for "capabilities" method call of an interface. */ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; struct wpa_driver_capa capa; int res; DBusMessageIter iter, iter_dict; char **eap_methods; size_t num_items; dbus_bool_t strict = FALSE; DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_BOOLEAN, &strict, DBUS_TYPE_INVALID)) strict = FALSE; reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) goto error; /* EAP methods */ eap_methods = eap_get_names_as_string_array(&num_items); if (eap_methods) { dbus_bool_t success = FALSE; size_t i = 0; success = wpa_dbus_dict_append_string_array( &iter_dict, "eap", (const char **) eap_methods, num_items); /* free returned method array */ while (eap_methods[i]) os_free(eap_methods[i++]); os_free(eap_methods); if (!success) goto error; } res = wpa_drv_get_capa(wpa_s, &capa); /***** pairwise cipher */ if (res < 0) { if (!strict) { const char *args[] = {"CCMP", "TKIP", "NONE"}; if (!wpa_dbus_dict_append_string_array( &iter_dict, "pairwise", args, sizeof(args) / sizeof(char*))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "CCMP")) goto error; } if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "TKIP")) goto error; } if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "NONE")) goto error; } if (!wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; } /***** group cipher */ if (res < 0) { if (!strict) { const char *args[] = { "CCMP", "TKIP", "WEP104", "WEP40" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "group", args, sizeof(args) / sizeof(char*))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group", &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "CCMP")) goto error; } if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "TKIP")) goto error; } if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "WEP104")) goto error; } if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "WEP40")) goto error; } if (!wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; } /***** key management */ if (res < 0) { if (!strict) { const char *args[] = { "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", "NONE" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "key_mgmt", args, sizeof(args) / sizeof(char*))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; if (!wpa_dbus_dict_string_array_add_element(&iter_array, "NONE")) goto error; if (!wpa_dbus_dict_string_array_add_element(&iter_array, "IEEE8021X")) goto error; if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "WPA-EAP")) goto error; } if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "WPA-PSK")) goto error; } if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "WPA-NONE")) goto error; } if (!wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; } /***** WPA protocol */ if (res < 0) { if (!strict) { const char *args[] = { "RSN", "WPA" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "proto", args, sizeof(args) / sizeof(char*))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "RSN")) goto error; } if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "WPA")) goto error; } if (!wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; } /***** auth alg */ if (res < 0) { if (!strict) { const char *args[] = { "OPEN", "SHARED", "LEAP" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "auth_alg", args, sizeof(args) / sizeof(char*))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "OPEN")) goto error; } if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "SHARED")) goto error; } if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "LEAP")) goto error; } if (!wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) goto error; } if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) goto error; return reply; error: if (reply) dbus_message_unref(reply); return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, "an internal error occurred returning " "interface capabilities."); } /** * wpas_dbus_iface_add_network - Add a new configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing the object path of the new network * * Handler function for "addNetwork" method call of a network interface. */ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; struct wpa_ssid *ssid; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { reply = dbus_message_new_error(message, WPAS_ERROR_ADD_NETWORK_ERROR, "wpa_supplicant could not add " "a network on this interface."); goto out; } wpas_notify_network_added(wpa_s, ssid); ssid->disabled = 1; wpa_config_set_network_defaults(ssid); /* Construct the object path for this network. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NETWORKS_PART "/%d", wpa_s->dbus_path, ssid->id); reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); out: return reply; } /** * wpas_dbus_iface_remove_network - Remove a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "removeNetwork" method call of a network interface. */ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; const char *op; char *iface = NULL, *net_id = NULL; int id; struct wpa_ssid *ssid; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } /* Extract the network ID */ iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); if (iface == NULL) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } /* Ensure the network is actually a child of this interface */ if (os_strcmp(iface, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } id = strtoul(net_id, NULL, 10); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } wpas_notify_network_removed(wpa_s, ssid); if (wpa_config_remove_network(wpa_s->conf, id) < 0) { reply = dbus_message_new_error(message, WPAS_ERROR_REMOVE_NETWORK_ERROR, "error removing the specified " "on this interface."); goto out; } if (ssid == wpa_s->current_ssid) wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); reply = wpas_dbus_new_success_reply(message); out: os_free(iface); os_free(net_id); return reply; } static const char *dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", "bssid", NULL }; static dbus_bool_t should_quote_opt(const char *key) { int i = 0; while (dont_quote[i] != NULL) { if (strcmp(key, dont_quote[i]) == 0) return FALSE; i++; } return TRUE; } /** * wpas_dbus_iface_set_network - Set options for a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network * Returns: a dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "set" method call of a configured network. */ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { DBusMessage *reply = NULL; struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; DBusMessageIter iter, iter_dict; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { char *value = NULL; size_t size = 50; int ret; if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } /* Type conversions, since wpa_supplicant wants strings */ if (entry.type == DBUS_TYPE_ARRAY && entry.array_type == DBUS_TYPE_BYTE) { if (entry.array_len <= 0) goto error; size = entry.array_len * 2 + 1; value = os_zalloc(size); if (value == NULL) goto error; ret = wpa_snprintf_hex(value, size, (u8 *) entry.bytearray_value, entry.array_len); if (ret <= 0) goto error; } else if (entry.type == DBUS_TYPE_STRING) { if (should_quote_opt(entry.key)) { size = os_strlen(entry.str_value); /* Zero-length option check */ if (size <= 0) goto error; size += 3; /* For quotes and terminator */ value = os_zalloc(size); if (value == NULL) goto error; ret = os_snprintf(value, size, "\"%s\"", entry.str_value); if (ret < 0 || (size_t) ret != (size - 1)) goto error; } else { value = os_strdup(entry.str_value); if (value == NULL) goto error; } } else if (entry.type == DBUS_TYPE_UINT32) { value = os_zalloc(size); if (value == NULL) goto error; ret = os_snprintf(value, size, "%u", entry.uint32_value); if (ret <= 0) goto error; } else if (entry.type == DBUS_TYPE_INT32) { value = os_zalloc(size); if (value == NULL) goto error; ret = os_snprintf(value, size, "%d", entry.int32_value); if (ret <= 0) goto error; } else goto error; if (wpa_config_set(ssid, entry.key, value, 0) < 0) goto error; if ((os_strcmp(entry.key, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) wpa_config_update_psk(ssid); else if (os_strcmp(entry.key, "priority") == 0) wpa_config_update_prio_list(wpa_s->conf); os_free(value); wpa_dbus_dict_entry_clear(&entry); continue; error: os_free(value); reply = wpas_dbus_new_invalid_opts_error(message, entry.key); wpa_dbus_dict_entry_clear(&entry); break; } if (!reply) reply = wpas_dbus_new_success_reply(message); out: return reply; } /** * wpas_dbus_iface_enable_network - Mark a configured network as enabled * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "enable" method call of a configured network. */ DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { wpa_supplicant_enable_network(wpa_s, ssid); return wpas_dbus_new_success_reply(message); } /** * wpas_dbus_iface_disable_network - Mark a configured network as disabled * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "disable" method call of a configured network. */ DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { wpa_supplicant_disable_network(wpa_s, ssid); return wpas_dbus_new_success_reply(message); } /** * wpas_dbus_iface_select_network - Attempt association with a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "selectNetwork" method call of network interface. */ DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; const char *op; struct wpa_ssid *ssid; char *iface_obj_path = NULL; char *network = NULL; if (os_strlen(dbus_message_get_signature(message)) == 0) { /* Any network */ ssid = NULL; } else { int nid; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } /* Extract the network number */ iface_obj_path = wpas_dbus_decompose_object_path(op, &network, NULL); if (iface_obj_path == NULL) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } /* Ensure the object path really points to this interface */ if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } nid = strtoul(network, NULL, 10); if (errno == EINVAL) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } ssid = wpa_config_get_network(wpa_s->conf, nid); if (ssid == NULL) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } } /* Finally, associate with the network */ wpa_supplicant_select_network(wpa_s, ssid); reply = wpas_dbus_new_success_reply(message); out: os_free(iface_obj_path); os_free(network); return reply; } /** * wpas_dbus_iface_disconnect - Terminate the current connection * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "disconnect" method call of network interface. */ DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s) { wpa_s->disconnected = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); return wpas_dbus_new_success_reply(message); } /** * wpas_dbus_iface_set_ap_scan - Control roaming mode * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "setAPScan" method call. */ DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; dbus_uint32_t ap_scan = 1; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan, DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } reply = wpas_dbus_new_success_reply(message); out: return reply; } /** * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Handler function for "setSmartcardModules" method call. */ DBusMessage * wpas_dbus_iface_set_smartcard_modules( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter, iter_dict; char *opensc_engine_path = NULL; char *pkcs11_engine_path = NULL; char *pkcs11_module_path = NULL; struct wpa_dbus_dict_entry entry; if (!dbus_message_iter_init(message, &iter)) goto error; if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (!strcmp(entry.key, "opensc_engine_path") && (entry.type == DBUS_TYPE_STRING)) { opensc_engine_path = os_strdup(entry.str_value); if (opensc_engine_path == NULL) goto error; } else if (!strcmp(entry.key, "pkcs11_engine_path") && (entry.type == DBUS_TYPE_STRING)) { pkcs11_engine_path = os_strdup(entry.str_value); if (pkcs11_engine_path == NULL) goto error; } else if (!strcmp(entry.key, "pkcs11_module_path") && (entry.type == DBUS_TYPE_STRING)) { pkcs11_module_path = os_strdup(entry.str_value); if (pkcs11_module_path == NULL) goto error; } else { wpa_dbus_dict_entry_clear(&entry); goto error; } wpa_dbus_dict_entry_clear(&entry); } os_free(wpa_s->conf->opensc_engine_path); wpa_s->conf->opensc_engine_path = opensc_engine_path; os_free(wpa_s->conf->pkcs11_engine_path); wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path; os_free(wpa_s->conf->pkcs11_module_path); wpa_s->conf->pkcs11_module_path = pkcs11_module_path; wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; wpa_supplicant_init_eapol(wpa_s); wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); return wpas_dbus_new_success_reply(message); error: os_free(opensc_engine_path); os_free(pkcs11_engine_path); os_free(pkcs11_module_path); return wpas_dbus_new_invalid_opts_error(message, NULL); } /** * wpas_dbus_iface_get_state - Get interface state * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing a STRING representing the current * interface state * * Handler function for "state" method call. */ DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; const char *str_state; reply = dbus_message_new_method_return(message); if (reply != NULL) { str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state, DBUS_TYPE_INVALID); } return reply; } /** * wpas_dbus_iface_get_scanning - Get interface scanning state * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing whether the interface is scanning * * Handler function for "scanning" method call. */ DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; reply = dbus_message_new_method_return(message); if (reply != NULL) { dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, DBUS_TYPE_INVALID); } else { wpa_printf(MSG_ERROR, "dbus: Not enough memory to return " "scanning state"); } return reply; } /** * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Asks wpa_supplicant to internally store a one or more binary blobs. */ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; DBusMessageIter iter, iter_dict; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) return wpas_dbus_new_invalid_opts_error(message, NULL); while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { struct wpa_config_blob *blob; if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); break; } if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) { reply = wpas_dbus_new_invalid_opts_error( message, "Byte array expected."); break; } if ((entry.array_len <= 0) || (entry.array_len > 65536) || !strlen(entry.key)) { reply = wpas_dbus_new_invalid_opts_error( message, "Invalid array size."); break; } blob = os_zalloc(sizeof(*blob)); if (blob == NULL) { reply = dbus_message_new_error( message, WPAS_ERROR_ADD_ERROR, "Not enough memory to add blob."); break; } blob->data = os_zalloc(entry.array_len); if (blob->data == NULL) { reply = dbus_message_new_error( message, WPAS_ERROR_ADD_ERROR, "Not enough memory to add blob data."); os_free(blob); break; } blob->name = os_strdup(entry.key); blob->len = entry.array_len; os_memcpy(blob->data, (u8 *) entry.bytearray_value, entry.array_len); if (blob->name == NULL || blob->data == NULL) { wpa_config_free_blob(blob); reply = dbus_message_new_error( message, WPAS_ERROR_ADD_ERROR, "Error adding blob."); break; } /* Success */ if (!wpa_config_remove_blob(wpa_s->conf, blob->name)) wpas_notify_blob_removed(wpa_s, blob->name); wpa_config_set_blob(wpa_s->conf, blob); wpas_notify_blob_added(wpa_s, blob->name); wpa_dbus_dict_entry_clear(&entry); } wpa_dbus_dict_entry_clear(&entry); return reply ? reply : wpas_dbus_new_success_reply(message); } /** * wpas_dbus_iface_remove_blob - Remove named binary blobs * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure * Returns: A dbus message containing a UINT32 indicating success (1) or * failure (0) * * Asks wpa_supplicant to remove one or more previously stored binary blobs. */ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter, array; char *err_msg = NULL; dbus_message_iter_init(message, &iter); if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) return wpas_dbus_new_invalid_opts_error(message, NULL); dbus_message_iter_recurse(&iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *name; dbus_message_iter_get_basic(&array, &name); if (!os_strlen(name)) err_msg = "Invalid blob name."; if (wpa_config_remove_blob(wpa_s->conf, name) != 0) err_msg = "Error removing blob."; else wpas_notify_blob_removed(wpa_s, name); dbus_message_iter_next(&array); } if (err_msg) return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, err_msg); return wpas_dbus_new_success_reply(message); } /** * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure * Returns: a dbus message containing a UINT32 indicating success (1) or * failure (0), or returns a dbus error message with more information * * Handler function for "flush" method call. Handles requests for an * interface with an optional "age" parameter that specifies the minimum * age of a BSS to be flushed. */ DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, struct wpa_supplicant *wpa_s) { int flush_age = 0; if (os_strlen(dbus_message_get_signature(message)) != 0 && !dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &flush_age, DBUS_TYPE_INVALID)) { return wpas_dbus_new_invalid_opts_error(message, NULL); } if (flush_age == 0) wpa_bss_flush(wpa_s); else wpa_bss_flush_by_age(wpa_s, flush_age); return wpas_dbus_new_success_reply(message); }