hostapd/wpa_supplicant/dbus/dbus_old_handlers.c
Dan Williams 6aeeb6fa21 dbus: clean up new D-Bus interface getters and setters
A number of fixes/improvements here:

1) Remove casting of getter/setter function types which allows
us to change the prototypes in the future and not have hard-to-find
runtime segfaults

2) Instead of having the getters create a fake reply message which
then gets its arguments copied into the real reply message, and is
then disposed, just pass message iters around and have them add
their arguments to the message itself

3) For setters, just pass in the message iter positioned at the
start of the argument list, instead of each setter having to skip
over the standard interface+property name

4) Convert error handling to use DBusError and return the error
back down through the call stacks to the function that will
actually send the error back to the caller, instead of having a
fake DBusMessage of type DBUS_MESSAGE_TYPE_ERROR that then
needs to have the error extracted from it.

But most of all, this fixes various segfaults (like rh #725517
and #678625) which were caused by some functions deep down in the
getter callpaths wanting a source DBusMessage* when the getters were
used for two things: signals (which don't have a source DBusMessage)
and methods (which will have a source DBusMessage that's being
replied to).  This duality made the code fragile when handling
errors like invalid IEs over the air.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2011-07-29 21:25:39 +03:00

1468 lines
41 KiB
C

/*
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
* 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 <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)) {
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);
}