Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/iwd.network.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ The group ``[Settings]`` contains general settings.
Properly configured Access Points will typically update this setting
appropriately via Transition Disable indications. User customization
of this value is thus typically not required.
* - ExternallyManaged
- Values: true, **false**

If enabled, the profile contents are managed by an external
application. IWD will read the profile but will not update it as a
side effect of network operation, for example to persist connection
metadata or security material learned at runtime. Explicit profile
removal through the KnownNetwork Forget method is still allowed.
* - UseDefaultEccGroup
- Values: true, false

Expand Down
8 changes: 8 additions & 0 deletions src/knownnetworks.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ void __network_config_parse(const struct l_settings *settings,

config->always_random_addr = b;

if (!l_settings_get_bool(settings, NET_EXTERNALLY_MANAGED, &b))
b = false;

config->externally_managed = b;

value = l_settings_get_value(settings, NET_ADDRESS_OVERRIDE);
if (value) {
if (util_string_to_address(value, new_addr) &&
Expand Down Expand Up @@ -690,6 +695,9 @@ static struct l_dbus_message *known_network_property_set_autoconnect(
if (network->config.is_autoconnectable == autoconnect)
return l_dbus_message_new_method_return(message);

if (network->config.externally_managed)
return dbus_error_not_available(message);

settings = network->ops->open(network);
if (!settings)
return dbus_error_failed(message);
Expand Down
2 changes: 2 additions & 0 deletions src/knownnetworks.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#define NET_TRANSITION_DISABLE SETTINGS, "TransitionDisable"
#define NET_TRANSITION_DISABLE_MODES SETTINGS, "DisabledTransitionModes"
#define NET_USE_DEFAULT_ECC_GROUP SETTINGS, "UseDefaultEccGroup"
#define NET_EXTERNALLY_MANAGED SETTINGS, "ExternallyManaged"

enum security;
struct scan_freq_set;
Expand Down Expand Up @@ -82,6 +83,7 @@ struct network_config {
bool have_transition_disable : 1;
uint8_t transition_disable;
enum known_network_ecc_group ecc_group;
bool externally_managed : 1;
};

struct network_info {
Expand Down
67 changes: 50 additions & 17 deletions src/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,29 @@ static bool network_settings_load(struct network *network)
return network->settings != NULL;
}

static bool network_settings_are_externally_managed(
struct l_settings *settings)
{
bool externally_managed;

if (!settings)
return false;

if (!l_settings_get_bool(settings, NET_EXTERNALLY_MANAGED,
&externally_managed))
return false;

return externally_managed;
}

static bool network_is_externally_managed(struct network *network)
{
if (network->info)
return network->info->config.externally_managed;

return network_settings_are_externally_managed(network->settings);
}

static void network_reset_psk(struct network *network)
{
if (network->psk)
Expand Down Expand Up @@ -180,25 +203,27 @@ void network_connected(struct network *network)
const char *ssid = network_get_ssid(network);
int err;

if (!network->info) {
/*
* This is an open network seen for the first time:
*
* Write a settings file to keep track of the
* last connected time. This will also make iwd autoconnect
* to this network in the future.
*/
if (!network->settings)
network->settings = l_settings_new();
if (!network_is_externally_managed(network)) {
if (!network->info) {
/*
* This is an open network seen for the first time:
*
* Write a settings file to keep track of the
* last connected time. This will also make iwd
* autoconnect to this network in the future.
*/
if (!network->settings)
network->settings = l_settings_new();

storage_network_sync(security, ssid, network->settings);
} else {
err = network_info_touch(network->info);
if (err < 0)
l_error("Error %i touching network config", err);
storage_network_sync(security, ssid, network->settings);
} else {
err = network_info_touch(network->info);
if (err < 0)
l_error("Error %i touching network config", err);

/* Syncs frequencies of already known network*/
known_network_frequency_sync(network->info);
/* Syncs frequencies of already known network*/
known_network_frequency_sync(network->info);
}
}

l_queue_foreach_remove(network->secrets,
Expand Down Expand Up @@ -758,6 +783,9 @@ void network_sync_settings(struct network *network)

network->sync_settings = false;

if (network_is_externally_managed(network))
return;

/*
* Re-open the settings from Disk, in case they were updated
* since we last opened them.
Expand All @@ -768,6 +796,11 @@ void network_sync_settings(struct network *network)
if (L_WARN_ON(!fs_settings))
return;

if (network_settings_are_externally_managed(fs_settings)) {
l_settings_free(fs_settings);
return;
}

network_settings_save(network, fs_settings);
info->ops->sync(info, fs_settings);
l_settings_free(fs_settings);
Expand Down
17 changes: 16 additions & 1 deletion src/storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,10 +559,22 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
return 0;
}

static bool storage_settings_are_externally_managed(
struct l_settings *settings)
{
bool externally_managed;

if (!l_settings_get_bool(settings, "Settings", "ExternallyManaged",
&externally_managed))
return false;

return externally_managed;
}

/*
* Decrypts a network profile (if needed). If profile encryption is enabled
* and the profile is unencrypted it will be encrypted and written back to
* the file system automatically.
* the file system automatically, unless [Settings].ExternallyManaged is true.
*
* 'name' is used for decryption and is used as part of the IV. Callers
* should provide a unique identifier here if available. For example, the
Expand All @@ -581,6 +593,9 @@ bool storage_decrypt(struct l_settings *settings, const char *path,
if (!needs_encryption)
return true;

if (storage_settings_are_externally_managed(settings))
return true;

/* Profile never encrypted before. Encrypt and write to disk */
encrypted = __storage_encrypt(settings, name, &elen);
if (!encrypted) {
Expand Down
114 changes: 112 additions & 2 deletions unit/test-storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#endif

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ell/ell.h>

Expand All @@ -35,6 +38,49 @@ static bool aes_ctr_supported(const void *data)
return l_cipher_is_supported(L_CIPHER_AES_CTR);
}

static char *create_profile_file(const char *data)
{
char path[] = "/tmp/iwd-storage-test-XXXXXX";
size_t len = strlen(data);
int fd;

fd = mkstemp(path);
assert(fd >= 0);

assert(write(fd, data, len) == (ssize_t) len);
assert(close(fd) == 0);

return l_strdup(path);
}

static char *read_profile_file(const char *path)
{
char buffer[4096];
ssize_t len;

len = read_file(buffer, sizeof(buffer) - 1, "%s", path);
assert(len >= 0);

buffer[len] = '\0';

return l_strdup(buffer);
}

static struct l_settings *create_unencrypted_psk_settings(
bool externally_managed)
{
struct l_settings *settings = l_settings_new();

if (externally_managed)
l_settings_set_bool(settings, "Settings",
"ExternallyManaged", true);

l_settings_set_string(settings, "Security", "Passphrase",
"test-password");

return settings;
}

static void test_short_encrypted_bytes(const void *data)
{
struct l_settings *settings = l_settings_new();
Expand All @@ -54,13 +100,77 @@ static void test_short_encrypted_bytes(const void *data)
storage_exit();
}

static void test_decrypt_encrypts_profile(const void *data)
{
static const char profile[] =
"[Security]\n"
"Passphrase=test-password\n";
struct l_settings *settings;
char *path;
char *contents;

storage_init((const uint8_t *)"abc123", 6);

settings = create_unencrypted_psk_settings(false);
path = create_profile_file(profile);

assert(storage_decrypt(settings, path, "mySSID"));

contents = read_profile_file(path);
assert(strstr(contents, "EncryptedSecurity="));
assert(!strstr(contents, "Passphrase=test-password"));

l_free(contents);
unlink(path);
l_free(path);
l_settings_free(settings);

storage_exit();
}

static void test_decrypt_externally_managed_profile(const void *data)
{
static const char profile[] =
"[Settings]\n"
"ExternallyManaged=true\n"
"\n"
"[Security]\n"
"Passphrase=test-password\n";
struct l_settings *settings;
char *path;
char *contents;

storage_init((const uint8_t *)"abc123", 6);

settings = create_unencrypted_psk_settings(true);
path = create_profile_file(profile);

assert(storage_decrypt(settings, path, "mySSID"));

contents = read_profile_file(path);
assert(!strcmp(contents, profile));

l_free(contents);
unlink(path);
l_free(path);
l_settings_free(settings);

storage_exit();
}

int main(int argc, char *argv[])
{
l_test_init(&argc, &argv);

l_test_add_func_precheck("/storage/profile encryption",
test_short_encrypted_bytes,
aes_ctr_supported, 0);
test_short_encrypted_bytes,
aes_ctr_supported, 0);
l_test_add_func_precheck("/storage/profile encryption write",
test_decrypt_encrypts_profile,
aes_ctr_supported, 0);
l_test_add_func_precheck("/storage/externally managed profile",
test_decrypt_externally_managed_profile,
aes_ctr_supported, 0);

return l_test_run();
}
Loading