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 <damiendejean@chromium.org>
This commit is contained in:
Damien Dejean 2021-12-08 07:45:31 +00:00 committed by Jouni Malinen
parent 4262e6ca49
commit b44e199676
6 changed files with 366 additions and 0 deletions

View file

@ -552,6 +552,28 @@ fi.w1.wpa_supplicant1.CreateInterface.
<h3>AbortScan ( ) --> nothing</h3>
<p>Abort ongoing scan operation.</p>
</li>
<li>
<h3>AddCred ( a{sv} : args ) --> o : path</h3>
<p>Add an Interworking/Hotspot 2.0 credential.</p>
<h4>Arguments</h4>
<dl>
<dt>a{sv} : args</dt>
<dd>A dictionary with credential configuration. Dictionary entries are equivalent to entries in the "cred" block in wpa_supplicant configuration file.</dd>
</dl>
<h4>Returns</h4>
<dl>
<dt>o : path</dt>
<dd>A D-Bus path to an object representing the added credential</dd>
</dl>
</li>
<li>
<h3>RemoveCred ( o : path ) --> nothing</h3>
<p>Remove the specified Interworking/Hotspot 2.0 credential.</p>
</li>
<li>
<h3>RemoveAllCreds ( ) --> nothing</h3>
<p>Remove all configured Interworking/Hotspot 2.0 credentials.</p>
</li>
<li>
<h3>EAPLogoff ( ) --> nothing</h3>
<p>IEEE 802.1X EAPOL state machine logoff.</p>

View file

@ -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")

View file

@ -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 } }
};

View file

@ -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"

View file

@ -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

View file

@ -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);