From b44e199676d927d021d0de8db7ee5c2dec1e2db4 Mon Sep 17 00:00:00 2001 From: Damien Dejean Date: Wed, 8 Dec 2021 07:45:31 +0000 Subject: [PATCH] D-Bus: Interworking/Hotspot 2.0 credential operations Add "AddCred", "RemoveCred", and "RemoveAllCreds" methods to the D-Bus API of the network interface to allow the caller to manipulate a set of Interworking credentials similarly to the way this was enabled through the control interface. Signed-off-by: Damien Dejean --- doc/dbus.doxygen | 22 ++ tests/hwsim/test_dbus.py | 45 ++++ wpa_supplicant/dbus/dbus_new.c | 23 +++ wpa_supplicant/dbus/dbus_new.h | 3 + wpa_supplicant/dbus/dbus_new_handlers.c | 264 ++++++++++++++++++++++++ wpa_supplicant/dbus/dbus_new_handlers.h | 9 + 6 files changed, 366 insertions(+) diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen index 8231aac41..68fc84572 100644 --- a/doc/dbus.doxygen +++ b/doc/dbus.doxygen @@ -552,6 +552,28 @@ fi.w1.wpa_supplicant1.CreateInterface.

AbortScan ( ) --> nothing

Abort ongoing scan operation.

+
  • +

    AddCred ( a{sv} : args ) --> o : path

    +

    Add an Interworking/Hotspot 2.0 credential.

    +

    Arguments

    +
    +
    a{sv} : args
    +
    A dictionary with credential configuration. Dictionary entries are equivalent to entries in the "cred" block in wpa_supplicant configuration file.
    +
    +

    Returns

    +
    +
    o : path
    +
    A D-Bus path to an object representing the added credential
    +
    +
  • +
  • +

    RemoveCred ( o : path ) --> nothing

    +

    Remove the specified Interworking/Hotspot 2.0 credential.

    +
  • +
  • +

    RemoveAllCreds ( ) --> nothing

    +

    Remove all configured Interworking/Hotspot 2.0 credentials.

    +
  • EAPLogoff ( ) --> nothing

    IEEE 802.1X EAPOL state machine logoff.

    diff --git a/tests/hwsim/test_dbus.py b/tests/hwsim/test_dbus.py index 1143802c6..4edd0fd73 100644 --- a/tests/hwsim/test_dbus.py +++ b/tests/hwsim/test_dbus.py @@ -6091,3 +6091,48 @@ def test_dbus_roam(dev, apdev): with TestDbusConnect(bus) as t: if not t.success(): raise Exception("Expected signals not seen") + +def test_dbus_creds(dev, apdev): + "D-Bus interworking credentials" + (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0]) + iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE) + + args = {'domain': 'server.w1.fi', + 'realm': 'server.w1.fi', + 'roaming_consortium': '50a9bf', + 'required_roaming_consortium': '23bf50', + 'eap': 'TTLS', + 'phase2': 'auth=MSCHAPV2', + 'username': 'user', + 'password': 'password', + 'domain_suffix_match': 'server.w1.fi', + 'ca_cert': 'auth_serv/ca.pem'} + + path = iface.AddCred(dbus.Dictionary(args, signature='sv')) + for k, v in args.items(): + if k == 'password': + continue + prop = dev[0].get_cred(0, k) + if prop != v: + raise Exception('Credential add failed: %s does not match %s' % (prop, v)) + + iface.RemoveCred(path) + if not "FAIL" in dev[0].get_cred(0, 'domain'): + raise Exception("Credential remove failed") + + # Removal of multiple credentials + cred1 = {'domain': 'server1.w1.fi','realm': 'server1.w1.fi','eap': 'TTLS'} + iface.AddCred(dbus.Dictionary(cred1, signature='sv')) + if "FAIL" in dev[0].get_cred(0, 'domain'): + raise Exception("Failed to add credential") + + cred2 = {'domain': 'server2.w1.fi','realm': 'server2.w1.fi','eap': 'TTLS'} + iface.AddCred(dbus.Dictionary(cred2, signature='sv')) + if "FAIL" in dev[0].get_cred(1, 'domain'): + raise Exception("Failed to add credential") + + iface.RemoveAllCreds() + if not "FAIL" in dev[0].get_cred(0, 'domain'): + raise Exception("Credential remove failed") + if not "FAIL" in dev[0].get_cred(1, 'domain'): + raise Exception("Credential remove failed") diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 2c01943f7..bfa49d654 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -3570,6 +3570,29 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, +#ifdef CONFIG_INTERWORKING + { "AddCred", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_add_cred, + { + { "args", "a{sv}", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "RemoveCred", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_remove_cred, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "RemoveAllCreds", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_remove_all_creds, + { + END_ARGS + } + }, +#endif /* CONFIG_INTERWORKING */ { NULL, NULL, NULL, { END_ARGS } } }; diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 42db3892e..6cdd9dc16 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -96,6 +96,9 @@ enum wpas_dbus_sta_prop { #define WPAS_DBUS_NEW_P2P_PEERS_PART "Peers" #define WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer" +#define WPAS_DBUS_NEW_CREDENTIALS_PART "Credentials" +#define WPAS_DBUS_NEW_IFACE_CREDENTIAL WPAS_DBUS_NEW_INTERFACE ".Credential" + /* Top-level Errors */ #define WPAS_DBUS_ERROR_UNKNOWN_ERROR \ WPAS_DBUS_NEW_INTERFACE ".UnknownError" diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index db9f30c9a..ce6923e95 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -148,6 +148,9 @@ static const char * const dont_quote[] = { #ifdef CONFIG_P2P "go_p2p_dev_addr", "p2p_client_list", "psk_list", #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + "roaming_consortium", "required_roaming_consortium", +#endif /* CONFIG_INTERWORKING */ NULL }; @@ -328,6 +331,110 @@ error: } +/** + * set_cred_properties - Set the properties of a configured credential + * @wpa_s: wpa_supplicant structure for a network interface + * @cred: wpa_cred structure for a configured credential + * @iter: DBus message iterator containing dictionary of network + * properties to set. + * @error: On failure, an error describing the failure + * Returns: TRUE if the request succeeds, FALSE if it failed + */ +static dbus_bool_t set_cred_properties(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + DBusMessageIter *iter, + DBusError *error) +{ + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + DBusMessageIter iter_dict; + char *value = NULL; + + if (!wpa_dbus_dict_open_read(iter, &iter_dict, error)) + return FALSE; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + size_t size = 50; + int ret; + + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + value = NULL; + 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) + 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); + + size += 3; + value = os_zalloc(size); + if (!value) + goto error; + + ret = os_snprintf(value, size, "\"%s\"", + entry.str_value); + if (os_snprintf_error(size, ret)) + goto error; + } else { + value = os_strdup(entry.str_value); + if (!value) + goto error; + } + } else if (entry.type == DBUS_TYPE_UINT32) { + value = os_zalloc(size); + if (!value) + goto error; + + ret = os_snprintf(value, size, "%u", + entry.uint32_value); + if (os_snprintf_error(size, ret)) + goto error; + } else if (entry.type == DBUS_TYPE_INT32) { + value = os_zalloc(size); + if (!value) + goto error; + + ret = os_snprintf(value, size, "%d", + entry.int32_value); + if (os_snprintf_error(size, ret)) + goto error; + } else { + goto error; + } + + ret = wpa_config_set_cred(cred, entry.key, value, 0); + if (ret < 0) + goto error; + + os_free(value); + value = NULL; + wpa_dbus_dict_entry_clear(&entry); + } + + return TRUE; + +error: + os_free(value); + wpa_dbus_dict_entry_clear(&entry); + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; +} + + /** * wpas_dbus_simple_property_getter - Get basic type property * @iter: Message iter to use when appending arguments @@ -1515,6 +1622,163 @@ DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message, } +/** + * wpas_dbus_new_iface_add_cred - Add a new credential + * @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 credential + * + * Handler function for "AddCred" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_add_cred(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_cred *cred = NULL; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; + DBusError error; + + dbus_message_iter_init(message, &iter); + + if (wpa_s->dbus_new_path) + cred = wpa_config_add_cred(wpa_s->conf); + if (!cred) { + wpa_printf(MSG_ERROR, "%s[dbus]: can't add new credential.", + __func__); + reply = wpas_dbus_error_unknown_error( + message, + "wpa_supplicant could not add a credential on this interface."); + goto err; + } + + dbus_error_init(&error); + if (!set_cred_properties(wpa_s, cred, &iter, &error)) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: control interface couldn't set credential properties", + __func__); + reply = wpas_dbus_reply_new_from_error(message, &error, + DBUS_ERROR_INVALID_ARGS, + "Failed to add credential"); + dbus_error_free(&error); + goto err; + } + + /* Construct the object path for this network. */ + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_CREDENTIALS_PART "/%d", + wpa_s->dbus_new_path, cred->id); + + reply = dbus_message_new_method_return(message); + if (!reply) { + reply = wpas_dbus_error_no_memory(message); + goto err; + } + if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + dbus_message_unref(reply); + reply = wpas_dbus_error_no_memory(message); + goto err; + } + + return reply; + +err: + if (cred) + wpa_config_remove_cred(wpa_s->conf, cred->id); + return reply; +} + + +/** + * wpas_dbus_handler_remove_cred - Remove a configured credential + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemoveCred" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_remove_cred(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + char *iface, *cred_id; + int id; + struct wpa_cred *cred; + + dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID); + + /* Extract the network ID and ensure the network is actually a child of + * this interface */ + iface = wpas_dbus_new_decompose_object_path( + op, WPAS_DBUS_NEW_CREDENTIALS_PART, &cred_id); + if (!iface || !cred_id || !wpa_s->dbus_new_path || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + errno = 0; + id = strtoul(cred_id, NULL, 10); + if (errno != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + cred = wpa_config_get_cred(wpa_s->conf, id); + if (!cred) { + wpa_printf(MSG_ERROR, "%s[dbus]: could not find credential %s", + __func__, op); + reply = wpas_dbus_error_invalid_args( + message, "could not find credential"); + goto out; + } + + if (wpas_remove_cred(wpa_s, cred) < 0) { + wpa_printf(MSG_ERROR, + "%s[dbus]: error occurred when removing cred %d", + __func__, id); + reply = wpas_dbus_error_unknown_error( + message, + "error removing the specified credential on its interface."); + goto out; + } + +out: + os_free(iface); + return reply; +} + + +/** + * wpas_dbus_handler_remove_all_creds - Remove all the configured credentials + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "RemoveAllCreds" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_remove_all_creds(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + int res; + DBusMessage *reply = NULL; + + res = wpas_remove_all_creds(wpa_s); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s[dbus]: failed to remove all credentials", + __func__); + reply = wpas_dbus_error_unknown_error( + message, "failed to remove all credentials"); + } + + return reply; +} + + /** * wpas_dbus_handler_signal_poll - Request immediate signal properties * @message: Pointer to incoming dbus message diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index c36383f05..1496f5535 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -144,6 +144,15 @@ DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_add_cred(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_cred(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_all_creds(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DECLARE_ACCESSOR(wpas_dbus_getter_capabilities); DECLARE_ACCESSOR(wpas_dbus_getter_state); DECLARE_ACCESSOR(wpas_dbus_getter_scanning);