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/include/gsupplicant_types.h b/include/gsupplicant_types.h
index 34b55fa..9f86317 100644
--- a/include/gsupplicant_types.h
+++ b/include/gsupplicant_types.h
@@ -137,9 +137,11 @@ typedef enum gsupplicant_eap_method {
GSUPPLICANT_EAP_METHOD_PWD = (0x00040000)
} GSUPPLICANT_EAP_METHOD;
-typedef enum gsupplicant_auth_fags {
+typedef enum gsupplicant_auth_flags {
GSUPPLICANT_AUTH_DEFAULT = (0x00000000),
- GSUPPLICANT_AUTH_PHASE2_AUTHEAP = (0x00000001)
+ GSUPPLICANT_AUTH_PHASE2_AUTHEAP = (0x00000001),
+ GSUPPLICANT_AUTH_PHASE1_PEAPV0 = (0x00000002), /* Since 1.0.12 */
+ GSUPPLICANT_AUTH_PHASE1_PEAPV1 = (0x00000004) /* Since 1.0.12 */
} GSUPPLICANT_AUTH_FLAGS;
typedef enum gsupplicant_op_mode {
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..d28d336 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,8 +431,25 @@ static
void
gsupplicant_interface_add_network_args_security_peap(
GVariantBuilder* builder,
- const GSupplicantNetworkParams* np)
-{
+ const GSupplicantNetworkParams* np,
+ GHashTable* blobs)
+{
+ if (np->eap == GSUPPLICANT_EAP_METHOD_PEAP) {
+ switch (np->auth_flags &
+ (GSUPPLICANT_AUTH_PHASE1_PEAPV0 |
+ GSUPPLICANT_AUTH_PHASE1_PEAPV1)) {
+ case GSUPPLICANT_AUTH_PHASE1_PEAPV0:
+ gsupplicant_dict_add_string(builder, "phase1", "peapver=0");
+ break;
+ case GSUPPLICANT_AUTH_PHASE1_PEAPV1:
+ gsupplicant_dict_add_string(builder, "phase1", "peapver=1");
+ break;
+ default:
+ GWARN("Trying to force PEAPv0 and v1, ignoring");
+ case 0:
+ break;
+ }
+ }
/*
* Multiple protocols in phase2 should be allowed,
* e.g "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS
@@ -438,9 +457,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 +484,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 +511,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 +528,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 +544,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 +615,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 +652,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 +1194,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 +1295,7 @@ gsupplicant_interface_add_network_call_new(
GCancellable* cancel,
const GSupplicantNetworkParams* np,
guint flags,
+ GHashTable* blobs,
GSupplicantInterfaceStringResultFunc fn,
GDestroyNotify destroy,
void* data)
@@ -1275,7 +1306,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 +2470,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 +2904,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 +2960,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 +2990,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 +3039,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 +3087,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++) {