diff --git a/include/gsupplicant_interface.h b/include/gsupplicant_interface.h index d82f04a..2cbe88f 100644 --- a/include/gsupplicant_interface.h +++ b/include/gsupplicant_interface.h @@ -331,6 +331,23 @@ gsupplicant_interface_reattach( GSupplicantInterfaceResultFunc fn, void* data); +GCancellable* +gsupplicant_interface_add_blob( + GSupplicantInterface* self, + GCancellable* cancel, + const char* name, + GBytes* blob, + GSupplicantInterfaceResultFunc fn, + void* data); /* Since 1.0.12 */ + +GCancellable* +gsupplicant_interface_remove_blob( + GSupplicantInterface* iface, + GCancellable* cancel, + const char* name, + GSupplicantInterfaceResultFunc fn, + void* data); /* Since 1.0.12 */ + #define GSUPPLICANT_ADD_NETWORK_DELETE_OTHER (0x01) #define GSUPPLICANT_ADD_NETWORK_SELECT (0x02) #define GSUPPLICANT_ADD_NETWORK_ENABLE (0x04) @@ -353,6 +370,17 @@ gsupplicant_interface_add_network_full( GDestroyNotify destroy, void* data); +GCancellable* +gsupplicant_interface_add_network_full2( + GSupplicantInterface* iface, + GCancellable* cancel, + const GSupplicantNetworkParams* params, + guint flags, /* See above */ + GHashTable* blobs, /* char * => gbytes * */ + GSupplicantInterfaceStringResultFunc fn, + GDestroyNotify destroy, + void* data); /* Since 1.0.12 */ + GCancellable* gsupplicant_interface_select_network( GSupplicantInterface* iface, diff --git a/spec/fi.w1.wpa_supplicant1.Interface.xml b/spec/fi.w1.wpa_supplicant1.Interface.xml index bab8e08..83b8a67 100644 --- a/spec/fi.w1.wpa_supplicant1.Interface.xml +++ b/spec/fi.w1.wpa_supplicant1.Interface.xml @@ -31,7 +31,9 @@ - + + + diff --git a/src/gsupplicant_interface.c b/src/gsupplicant_interface.c index a1e107b..5ca58cc 100644 --- a/src/gsupplicant_interface.c +++ b/src/gsupplicant_interface.c @@ -86,6 +86,8 @@ typedef struct gsupplicant_interface_add_network_call { GCancellable* cancel; gulong cancel_id; GVariant* args; + GHashTable* blobs; + GHashTableIter iter; gboolean pending; GSupplicantInterfaceStringResultFunc fn; GDestroyNotify destroy; @@ -429,7 +431,8 @@ static void gsupplicant_interface_add_network_args_security_peap( GVariantBuilder* builder, - const GSupplicantNetworkParams* np) + const GSupplicantNetworkParams* np, + GHashTable* blobs) { /* * Multiple protocols in phase2 should be allowed, @@ -438,9 +441,11 @@ gsupplicant_interface_add_network_args_security_peap( */ if (np->phase2 != GSUPPLICANT_EAP_METHOD_NONE) { const char* ca_cert2 = - gsupplicant_check_abs_path(np->ca_cert_file2); + gsupplicant_check_blob_or_abs_path(np->ca_cert_file2, + blobs); const char* client_cert2 = - gsupplicant_check_abs_path(np->client_cert_file2); + gsupplicant_check_blob_or_abs_path(np->client_cert_file2, + blobs); const char* auth = (np->auth_flags & GSUPPLICANT_AUTH_PHASE2_AUTHEAP) ? "autheap" : "auth"; GString* buf = g_string_new(NULL); @@ -463,7 +468,8 @@ gsupplicant_interface_add_network_args_security_peap( if (client_cert2) { if (np->private_key_file2 && np->private_key_file2[0]) { const char* private_key2 = - gsupplicant_check_abs_path(np->private_key_file2); + gsupplicant_check_blob_or_abs_path(np->private_key_file2, + blobs); if (private_key2) { gsupplicant_dict_add_string(builder, "client_cert2", client_cert2); @@ -489,11 +495,14 @@ static void gsupplicant_interface_add_network_args_security_eap( GVariantBuilder* builder, - const GSupplicantNetworkParams* np) + const GSupplicantNetworkParams* np, + GHashTable* blobs) { guint found; - const char* ca_cert = gsupplicant_check_abs_path(np->ca_cert_file); - const char* client_cert = gsupplicant_check_abs_path(np->client_cert_file); + const char* ca_cert = + gsupplicant_check_blob_or_abs_path(np->ca_cert_file, blobs); + const char* client_cert = + gsupplicant_check_blob_or_abs_path(np->client_cert_file, blobs); const char* method = gsupplicant_eap_method_name(np->eap, &found); GASSERT(found == np->eap); /* Only one method should be specified */ gsupplicant_dict_add_string_ne(builder, "eap", method); @@ -503,7 +512,7 @@ gsupplicant_interface_add_network_args_security_eap( return; case GSUPPLICANT_EAP_METHOD_PEAP: case GSUPPLICANT_EAP_METHOD_TTLS: - gsupplicant_interface_add_network_args_security_peap(builder, np); + gsupplicant_interface_add_network_args_security_peap(builder, np, blobs); break; case GSUPPLICANT_EAP_METHOD_TLS: break; @@ -519,7 +528,8 @@ gsupplicant_interface_add_network_args_security_eap( if (client_cert) { if (np->private_key_file && np->private_key_file[0]) { const char* private_key = - gsupplicant_check_abs_path(np->private_key_file); + gsupplicant_check_blob_or_abs_path(np->private_key_file, + blobs); if (private_key) { gsupplicant_dict_add_string(builder, "client_cert", client_cert); @@ -589,7 +599,8 @@ gsupplicant_interface_add_network_args_security_proto( static GVariant* gsupplicant_interface_add_network_args_new( - const GSupplicantNetworkParams* np) + const GSupplicantNetworkParams* np, + GHashTable* blobs) { const char* key_mgmt = NULL; const char* auth_alg = NULL; @@ -625,7 +636,7 @@ gsupplicant_interface_add_network_args_new( case GSUPPLICANT_SECURITY_EAP: GDEBUG_("EAP security"); key_mgmt = "WPA-EAP"; - gsupplicant_interface_add_network_args_security_eap(&builder, np); + gsupplicant_interface_add_network_args_security_eap(&builder, np, blobs); gsupplicant_interface_add_network_args_security_proto(&builder, np); gsupplicant_interface_add_network_args_security_ciphers(&builder, np); break; @@ -1167,6 +1178,9 @@ gsupplicant_interface_add_network_call_free( if (call->cancel_id) { g_cancellable_disconnect(call->cancel, call->cancel_id); } + if (call->blobs) { + g_hash_table_unref(call->blobs); + } g_object_unref(call->cancel); g_free(call->path); if (call->destroy) { @@ -1265,6 +1279,7 @@ gsupplicant_interface_add_network_call_new( GCancellable* cancel, const GSupplicantNetworkParams* np, guint flags, + GHashTable* blobs, GSupplicantInterfaceStringResultFunc fn, GDestroyNotify destroy, void* data) @@ -1275,7 +1290,11 @@ gsupplicant_interface_add_network_call_new( call->cancel_id = g_cancellable_connect(call->cancel, G_CALLBACK(gsupplicant_interface_add_network_call_cancelled), call, NULL); - call->args = gsupplicant_interface_add_network_args_new(np); + if (blobs && g_hash_table_size(blobs)) { + call->blobs = g_hash_table_ref(blobs); + g_hash_table_iter_init(&call->iter, call->blobs); + } + call->args = gsupplicant_interface_add_network_args_new(np, call->blobs); call->iface = gsupplicant_interface_ref(iface); call->fn = fn; call->destroy = destroy; @@ -2435,6 +2454,80 @@ gsupplicant_interface_reattach( fi_w1_wpa_supplicant1_interface_call_reattach); } +static /* should be public? */ +GCancellable* +gsupplicant_interface_add_blob_full( + GSupplicantInterface* self, + GCancellable* cancel, + const char* name, + GBytes* blob, + GSupplicantInterfaceResultFunc fn, + GDestroyNotify free, + void* data) +{ + if (G_LIKELY(self) && self->valid && name && blob) { + GSupplicantInterfacePriv* priv = self->priv; + GSupplicantInterfaceCall* call = gsupplicant_interface_call_new(self, + cancel, gsupplicant_interface_call_finish_void, G_CALLBACK(fn), + free, data); + gsize size = 0; + const guint8* data = g_bytes_get_data(blob, &size); + fi_w1_wpa_supplicant1_interface_call_add_blob( + priv->proxy, + name, + g_variant_new_fixed_array( + G_VARIANT_TYPE_BYTE, data, size, 1), + call->cancel, + gsupplicant_interface_call_finished, call); + return call->cancel; + } + gsupplicant_cancel_later(cancel); + return NULL; +} + +GCancellable* +gsupplicant_interface_add_blob( + GSupplicantInterface* self, + GCancellable* cancel, + const char* name, + GBytes* blob, + GSupplicantInterfaceResultFunc fn, + void* data) +{ + return gsupplicant_interface_add_blob_full(self, cancel, name, blob, + fn, NULL, data); +} + +static /* should be public? */ +GCancellable* +gsupplicant_interface_remove_blob_full( + GSupplicantInterface* self, + GCancellable* cancel, + const char* name, + GSupplicantInterfaceResultFunc fn, + GDestroyNotify free, + void* data) +{ + if (name) { + return gsupplicant_interface_call_string_void(self, cancel, name, fn, + free, data, fi_w1_wpa_supplicant1_interface_call_remove_blob); + } + gsupplicant_cancel_later(cancel); + return NULL; +} + +GCancellable* +gsupplicant_interface_remove_blob( + GSupplicantInterface* self, + GCancellable* cancel, + const char* name, + GSupplicantInterfaceResultFunc fn, + void* data) +{ + return gsupplicant_interface_remove_blob_full(self, cancel, name, + fn, NULL, data); +} + static /* should be public? */ GCancellable* gsupplicant_interface_select_network_full( @@ -2795,6 +2888,47 @@ gsupplicant_interface_add_network1( } } +static +void +gsupplicant_interface_add_network_pre1( + GObject* obj, + GAsyncResult* result, + gpointer data) { + + GError* error = NULL; + GSupplicantInterfaceAddNetworkCall* call = data; + FiW1Wpa_supplicant1Interface* proxy = call->iface->priv->proxy; + GASSERT(proxy == FI_W1_WPA_SUPPLICANT1_INTERFACE(obj)); + + if (fi_w1_wpa_supplicant1_interface_call_add_blob_finish(proxy, + result, &error)) { + gpointer name, blob; + + if (g_hash_table_iter_next(&call->iter, &name, &blob)) { + gsize size = 0; + const guint8* data = g_bytes_get_data(blob, &size); + + fi_w1_wpa_supplicant1_interface_call_add_blob( + proxy, + name, + g_variant_new_fixed_array( + G_VARIANT_TYPE_BYTE, data, size, 1), + call->cancel, + gsupplicant_interface_add_network_pre1, call); + } else { + g_hash_table_unref(call->blobs); + call->blobs = NULL; + fi_w1_wpa_supplicant1_interface_call_add_network(proxy, + call->args, call->cancel, gsupplicant_interface_add_network1, + call); + g_variant_unref(call->args); + call->args = NULL; + } + } + if (error) { + g_error_free(error); + } +} static void gsupplicant_interface_add_network0( @@ -2810,10 +2944,27 @@ gsupplicant_interface_add_network0( result, &error)) { GVERBOSE_("removed all networks"); call->pending = TRUE; - fi_w1_wpa_supplicant1_interface_call_add_network(proxy, call->args, - call->cancel, gsupplicant_interface_add_network1, call); - g_variant_unref(call->args); - call->args = NULL; + if (call->blobs) { + gpointer name, blob; + gsize size = 0; + const guint8* data; + + g_hash_table_iter_next(&call->iter, &name, &blob); + data = g_bytes_get_data(blob, &size); + + fi_w1_wpa_supplicant1_interface_call_add_blob( + proxy, + name, + g_variant_new_fixed_array( + G_VARIANT_TYPE_BYTE, data, size, 1), + call->cancel, + gsupplicant_interface_add_network_pre1, call); + } else { + fi_w1_wpa_supplicant1_interface_call_add_network(proxy, call->args, + call->cancel, gsupplicant_interface_add_network1, call); + g_variant_unref(call->args); + call->args = NULL; + } } else { call->pending = FALSE; gsupplicant_interface_call_add_network_finish(call, error); @@ -2823,12 +2974,47 @@ gsupplicant_interface_add_network0( } } +static +void +gsupplicant_interface_add_network_pre0( + GObject* obj, + GAsyncResult* result, + gpointer data) +{ + GError* error = NULL; + GSupplicantInterfaceAddNetworkCall* call = data; + FiW1Wpa_supplicant1Interface* proxy = call->iface->priv->proxy; + GASSERT(proxy == FI_W1_WPA_SUPPLICANT1_INTERFACE(obj)); + if (fi_w1_wpa_supplicant1_interface_call_remove_blob_finish(proxy, + result, &error)) { + GVariantIter blobi; + GVariant* blobs = fi_w1_wpa_supplicant1_interface_get_blobs(proxy); + if (g_variant_iter_init(&blobi, blobs)) { + const char* blobn; + g_variant_iter_next(&blobi, "{&s@ay}", &blobn, NULL); + fi_w1_wpa_supplicant1_interface_call_remove_blob( + proxy, blobn, + call->cancel, gsupplicant_interface_add_network_pre0, + call); + } else { + fi_w1_wpa_supplicant1_interface_call_remove_all_networks( + proxy, call->cancel, gsupplicant_interface_add_network0, + call); + } + } else { + call->pending = FALSE; + gsupplicant_interface_call_add_network_finish(call, error); + } + g_clear_error(&error); +} + GCancellable* -gsupplicant_interface_add_network_full( +gsupplicant_interface_add_network_full2( GSupplicantInterface* self, GCancellable* cancel, const GSupplicantNetworkParams* np, guint flags, + GHashTable* blobs, GSupplicantInterfaceStringResultFunc fn, GDestroyNotify destroy, void* data) @@ -2837,18 +3023,47 @@ gsupplicant_interface_add_network_full( GSupplicantInterfacePriv* priv = self->priv; GSupplicantInterfaceAddNetworkCall* call = gsupplicant_interface_add_network_call_new(self, cancel, np, - flags, fn, destroy, data); + flags, blobs, fn, destroy, data); call->pending = TRUE; + if (flags & GSUPPLICANT_ADD_NETWORK_DELETE_OTHER) { - fi_w1_wpa_supplicant1_interface_call_remove_all_networks( - priv->proxy, call->cancel, gsupplicant_interface_add_network0, - call); + GVariantIter blobi; + GVariant* old_blobs = fi_w1_wpa_supplicant1_interface_get_blobs(priv->proxy); + if (g_variant_iter_init(&blobi, old_blobs)) { + const char *blobn; + g_variant_iter_next(&blobi, "{&s@ay}", &blobn, NULL); + fi_w1_wpa_supplicant1_interface_call_remove_blob( + priv->proxy, blobn, + call->cancel, gsupplicant_interface_add_network_pre0, + call); + } else { + fi_w1_wpa_supplicant1_interface_call_remove_all_networks( + priv->proxy, call->cancel, gsupplicant_interface_add_network0, + call); + } } else { - fi_w1_wpa_supplicant1_interface_call_add_network(priv->proxy, - call->args, call->cancel, gsupplicant_interface_add_network1, - call); - g_variant_unref(call->args); - call->args = NULL; + if (call->blobs) { + gpointer name, blob; + gsize size = 0; + const guint8* data; + + g_hash_table_iter_next(&call->iter, &name, &blob); + data = g_bytes_get_data(blob, &size); + + fi_w1_wpa_supplicant1_interface_call_add_blob( + priv->proxy, + name, + g_variant_new_fixed_array( + G_VARIANT_TYPE_BYTE, data, size, 1), + call->cancel, + gsupplicant_interface_add_network_pre1, call); + } else { + fi_w1_wpa_supplicant1_interface_call_add_network(priv->proxy, + call->args, call->cancel, gsupplicant_interface_add_network1, + call); + g_variant_unref(call->args); + call->args = NULL; + } } return call->cancel; } @@ -2856,6 +3071,20 @@ gsupplicant_interface_add_network_full( return NULL; } +GCancellable* +gsupplicant_interface_add_network_full( + GSupplicantInterface* self, + GCancellable* cancel, + const GSupplicantNetworkParams* np, + guint flags, + GSupplicantInterfaceStringResultFunc fn, + GDestroyNotify destroy, + void* data) +{ + return gsupplicant_interface_add_network_full2(self, cancel, np, flags, + NULL, fn, destroy, data); +} + GCancellable* gsupplicant_interface_add_network( GSupplicantInterface* self, diff --git a/src/gsupplicant_util.c b/src/gsupplicant_util.c index 8968976..e959396 100644 --- a/src/gsupplicant_util.c +++ b/src/gsupplicant_util.c @@ -290,6 +290,27 @@ gsupplicant_check_abs_path( return NULL; } +const char* +gsupplicant_check_blob_or_abs_path( + const char* path, + GHashTable* blobs) +{ + if (path && path[0]) { + const char* blob_prefix = "blob://"; + if (!g_str_has_prefix(path, blob_prefix)) { + return gsupplicant_check_abs_path(path); + } else if (blobs) { + if (g_hash_table_contains(blobs, path + strlen(blob_prefix))) { + return path; + } else { + GWARN("No such blob: %s", path); + return NULL; + } + } + } + return NULL; +} + int gsupplicant_dict_parse( GVariant* dict, diff --git a/src/gsupplicant_util_p.h b/src/gsupplicant_util_p.h index dce74a6..964aa51 100644 --- a/src/gsupplicant_util_p.h +++ b/src/gsupplicant_util_p.h @@ -120,6 +120,11 @@ const char* gsupplicant_check_abs_path( const char* path); +const char* +gsupplicant_check_blob_or_abs_path( + const char* path, + GHashTable* blobs); + int gsupplicant_dict_parse( GVariant* dict, diff --git a/test/test_util/test_util.c b/test/test_util/test_util.c index a5d4ab9..89f92f7 100644 --- a/test/test_util/test_util.c +++ b/test/test_util/test_util.c @@ -351,6 +351,47 @@ test_util_abs_path( g_free(name); } +/*==========================================================================* + * blob_or_abs_path + *==========================================================================*/ + +static +void +test_util_blob_or_abs_path( + void) +{ + char* name = NULL; + int fd; + GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal); + + g_assert(!gsupplicant_check_blob_or_abs_path(NULL, ht)); + g_assert(!gsupplicant_check_blob_or_abs_path(NULL, NULL)); + g_assert(!gsupplicant_check_blob_or_abs_path("", ht)); + g_assert(!gsupplicant_check_blob_or_abs_path("foo", ht)); + g_assert(!gsupplicant_check_blob_or_abs_path("blob://foo", NULL)); + g_assert(!gsupplicant_check_blob_or_abs_path("blob://foo", ht)); + + g_hash_table_insert(ht, "foo", "bar"); + g_assert(gsupplicant_check_blob_or_abs_path("blob://foo", ht)); + g_assert(!gsupplicant_check_blob_or_abs_path("blob://bar", ht)); + + fd = g_file_open_tmp("test-abspath-XXXXXX", &name, NULL); + g_assert(fd >= 0); + GDEBUG_("%s", name); + g_assert(close(fd) == 0); + + /* Now a file should exist (albeit empty), the check should pass */ + g_assert(gsupplicant_check_blob_or_abs_path(name, NULL)); + g_assert(gsupplicant_check_blob_or_abs_path(name, ht)); + + /* When the file is missing, the check should fail */ + g_assert(g_unlink(name) == 0); + g_assert(!gsupplicant_check_blob_or_abs_path(name, NULL)); + g_assert(!gsupplicant_check_blob_or_abs_path(name, ht)); + g_free(name); + g_hash_table_unref(ht); +} + /*==========================================================================* * dict_parse *==========================================================================*/ @@ -557,6 +598,7 @@ int main(int argc, char* argv[]) g_test_add_func(TEST_PREFIX "call_later", test_util_call_later); g_test_add_func(TEST_PREFIX "cancel_later", test_util_cancel_later); g_test_add_func(TEST_PREFIX "abs_path", test_util_abs_path); + g_test_add_func(TEST_PREFIX "blob_or_abs_path", test_util_blob_or_abs_path); g_test_add_func(TEST_PREFIX "dict_parse", test_util_dict_parse); g_test_add_func(TEST_PREFIX "utf8_from_bytes", test_util_utf8_from_bytes); for (i = 0; i < G_N_ELEMENTS(test_util_utf8_data); i++) {