Commit 66d9240c authored by Slava Monich's avatar Slava Monich

[connman] Introduced access control framework. JB#37923

So far it's limited to net.connman.Service GetProperty and SetProperty
requests but can be extended to cover other requests and actions when
it becomes necessary.
parent 948bccc8
......@@ -68,6 +68,7 @@ unit/test-ippool
unit/test-nat
unit/test-nat
unit/test-dnsproxy
unit/test-access
unit/test-sailfish_wakeup_timer
*.gcda
......
......@@ -12,7 +12,8 @@ include_HEADERS = include/log.h include/plugin.h \
include/storage.h include/provision.h \
include/session.h include/ipaddress.h include/agent.h \
include/inotify.h include/peer.h include/machine.h \
include/technology.h include/wakeup_timer.h
include/technology.h include/wakeup_timer.h \
include/access.h
nodist_include_HEADERS = include/version.h
......@@ -120,7 +121,8 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) \
src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c \
src/peer_service.c src/machine.c src/util.c \
src/wakeup_timer.c src/jolla-stats.c src/fsid.c
src/wakeup_timer.c src/jolla-stats.c src/fsid.c \
src/access.c
src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
......@@ -268,7 +270,7 @@ client_connmanctl_LDADD = gdbus/libgdbus-internal.la @DBUS_LIBS@ @GLIB_LIBS@ \
-lreadline -ldl
endif
noinst_PROGRAMS += unit/test-ippool \
noinst_PROGRAMS += unit/test-access unit/test-ippool \
unit/test-sailfish_wakeup_timer unit/test-dnsproxy
if TEST_COVERAGE
......@@ -281,6 +283,10 @@ unit_test_ippool_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_ippool_LDADD = gdbus/libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
unit_test_access_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_access_SOURCES = unit/test-access.c src/access.c src/log.c
unit_test_access_LDADD = @GLIB_LIBS@ -ldl
unit_test_sailfish_wakeup_timer_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_sailfish_wakeup_timer_SOURCES = unit/test-sailfish_wakeup_timer.c \
src/log.c src/wakeup_timer.c \
......@@ -290,7 +296,7 @@ unit_test_sailfish_wakeup_timer_LDADD = @GLIB_LIBS@ -lrt -ldl
unit_test_dnsproxy_SOURCES = unit/test-dnsproxy.c src/log.c
unit_test_dnsproxy_LDADD = @GLIB_LIBS@ -lresolv -ldl
TESTS = unit/test-ippool \
TESTS = unit/test-access unit/test-ippool \
unit/test-sailfish_wakeup_timer unit/test-dnsproxy
if WISPR
......
/*
* Connection Manager
*
* Copyright (C) 2017 Jolla Ltd. All rights reserved.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __CONNMAN_ACCESS_H
#define __CONNMAN_ACCESS_H
#include <glib.h>
G_BEGIN_DECLS
enum connman_access {
CONNMAN_ACCESS_DENY,
CONNMAN_ACCESS_ALLOW
};
struct connman_access_service_policy;
struct connman_access_service_policy_impl;
struct connman_access_driver {
const char *name;
const char *default_service_policy;
struct connman_access_service_policy_impl *(*service_policy_create)
(const char *spec);
void (*service_policy_free)
(struct connman_access_service_policy_impl *policy);
enum connman_access (*service_get_property)
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access);
enum connman_access (*service_set_property)
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access);
};
int connman_access_driver_register(const struct connman_access_driver *d);
void connman_access_timer_unregister(const struct connman_access_driver *d);
const char *connman_access_default_service_policy(void);
struct connman_access_service_policy *connman_access_service_policy_create(
const char *spec);
void connman_access_service_policy_free(
struct connman_access_service_policy *policy);
enum connman_access connman_access_service_get_property(
struct connman_access_service_policy *policy, const char *sender,
const char *name, enum connman_access default_access);
enum connman_access connman_access_service_set_property(
struct connman_access_service_policy *policy, const char *sender,
const char *name, enum connman_access default_access);
G_END_DECLS
#endif /* __CONNMAN_ACCESS_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
/*
* Connection Manager
*
* Copyright (C) 2017 Jolla Ltd. All rights reserved.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <connman/access.h>
#include <connman/log.h>
#include <errno.h>
#include <string.h>
struct connman_access_service_policy {
struct connman_access_service_policy_impl *impl;
const struct connman_access_driver *driver;
};
#define DRIVER_NAME_SEPARATOR ':'
#define DRIVER_NAME_SEPARATOR_STR ":"
static GSList *access_drivers;
static char *access_default_service_policy;
static void connman_access_driver_update_default_service_policy()
{
g_free(access_default_service_policy);
access_default_service_policy = NULL;
if (access_drivers) {
const struct connman_access_driver *default_driver =
access_drivers->data;
if (default_driver->default_service_policy) {
access_default_service_policy =
g_strconcat(default_driver->name,
DRIVER_NAME_SEPARATOR_STR,
default_driver->default_service_policy,
NULL);
}
}
}
int connman_access_driver_register(const struct connman_access_driver *drv)
{
if (!drv || !drv->name)
return -EINVAL;
if (g_slist_find(access_drivers, drv))
return -EALREADY;
/*
* If there were multiple drivers we would have to sort them somehow.
* For now let the last one to become the default.
*/
DBG("\"%s\"", drv->name);
access_drivers = g_slist_prepend(access_drivers, (void*)drv);
connman_access_driver_update_default_service_policy();
return 0;
}
void connman_access_timer_unregister(const struct connman_access_driver *drv)
{
if (g_slist_find(access_drivers, drv)) {
DBG("\"%s\"", drv->name);
access_drivers = g_slist_remove(access_drivers, drv);
connman_access_driver_update_default_service_policy();
}
}
const char *connman_access_default_service_policy()
{
return access_default_service_policy;
}
struct connman_access_service_policy *connman_access_service_policy_create(
const char *spec)
{
const struct connman_access_driver *driver = NULL;
struct connman_access_service_policy *p = NULL;
if (access_drivers) {
if (spec) {
/*
* The policy spec starts with the driver name
* followed by :
*/
GSList *l = access_drivers;
const char *name = spec;
const char *sep = strchr(spec, DRIVER_NAME_SEPARATOR);
gsize len;
if (sep) {
/* Skip the separator */
len = sep - spec;
spec = sep + 1;
} else {
/* Default policy of the specified driver */
len = strlen(spec);
spec = NULL;
}
while (l) {
const struct connman_access_driver *d =
l->data;
if (!strncmp(d->name, name, len) &&
!d->name[len]) {
driver = d;
break;
}
l = l->next;
}
if (!driver) {
DBG("no such access driver: %.*s", len, name);
}
} else {
driver = access_drivers->data;
}
}
if (driver && driver->service_policy_create) {
struct connman_access_service_policy_impl *impl =
driver->service_policy_create(spec);
if (impl) {
p = g_slice_new(struct connman_access_service_policy);
p->impl = impl;
p->driver = driver;
}
}
return p;
}
void connman_access_service_policy_free(
struct connman_access_service_policy *p)
{
if (p) {
if (p->driver->service_policy_free)
p->driver->service_policy_free(p->impl);
g_slice_free(struct connman_access_service_policy, p);
}
}
enum connman_access connman_access_service_get_property(
struct connman_access_service_policy *p, const char *sender,
const char *name, enum connman_access default_access)
{
if (p && p->driver->service_get_property)
return p->driver->service_get_property(p->impl, sender, name,
default_access);
return default_access;
}
enum connman_access connman_access_service_set_property(
struct connman_access_service_policy *p, const char *sender,
const char *name, enum connman_access default_access)
{
if (p && p->driver->service_set_property)
return p->driver->service_set_property(p->impl, sender, name,
default_access);
return default_access;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
......@@ -61,6 +61,7 @@ struct connman_config_service {
GSList *service_identifiers;
char *config_ident; /* file prefix */
char *config_entry; /* entry name */
char *access;
bool hidden;
bool virtual;
char *virtual_file;
......@@ -112,6 +113,7 @@ static bool cleanup = false;
#define SERVICE_KEY_PASSPHRASE "Passphrase"
#define SERVICE_KEY_SECURITY "Security"
#define SERVICE_KEY_HIDDEN "Hidden"
#define SERVICE_KEY_ACCESS "Access"
#define SERVICE_KEY_IPv4 "IPv4"
#define SERVICE_KEY_IPv6 "IPv6"
......@@ -156,6 +158,7 @@ static const char *service_possible_keys[] = {
SERVICE_KEY_SEARCH_DOMAINS,
SERVICE_KEY_TIMESERVERS,
SERVICE_KEY_DOMAIN,
SERVICE_KEY_ACCESS,
NULL,
};
......@@ -262,6 +265,7 @@ free_only:
g_free(config_service->config_ident);
g_free(config_service->config_entry);
g_free(config_service->virtual_file);
g_free(config_service->access);
g_free(config_service);
}
......@@ -462,6 +466,12 @@ static bool load_service_generic(GKeyFile *keyfile,
g_free(str);
}
str = __connman_config_get_string(keyfile, group, SERVICE_KEY_ACCESS, NULL);
if (str) {
g_free(service->access);
service->access = str;
}
str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv6_PRIVACY,
NULL);
if (str) {
......@@ -517,6 +527,7 @@ static bool load_service_generic(GKeyFile *keyfile,
return true;
err:
g_free(service->access);
g_free(service->ident);
g_free(service->type);
g_free(service->ipv4_address);
......@@ -1353,6 +1364,9 @@ static int try_provision_service(struct connman_config_service *config,
__connman_service_set_config(service, config->config_ident,
config->config_entry);
if (config->access)
__connman_service_set_access(service, config->access);
if (config->domain_name)
__connman_service_set_domainname(service, config->domain_name);
......
......@@ -832,6 +832,8 @@ void __connman_service_counter_unregister(const char *counter);
void __connman_service_counter_send_initial(const char *counter);
void __connman_service_counter_reset_all(const char *type);
void __connman_service_removed(const char *ident);
void __connman_service_set_access(struct connman_service *service,
const char *access);
#include <connman/peer.h>
......
......@@ -34,6 +34,7 @@
#include <connman/storage.h>
#include <connman/setting.h>
#include <connman/agent.h>
#include <connman/access.h>
#include "connman.h"
......@@ -149,6 +150,8 @@ struct connman_service {
guint online_check_timer_ipv6;
guint connect_retry_timer;
guint connect_retry_timeout;
struct connman_access_service_policy *policy;
char *access;
};
static bool allow_property_changed(struct connman_service *service);
......@@ -649,6 +652,13 @@ static int service_load(struct connman_service *service)
service->passphrase = str;
}
str = g_key_file_get_string(keyfile,
service->identifier, "Access", NULL);
if (str) {
__connman_service_set_access(service, str);
g_free(str);
}
if (service->ipconfig_ipv4)
__connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
service->identifier, "IPv4.");
......@@ -2527,6 +2537,21 @@ static void append_wifi_ext_info(DBusMessageIter *dict,
DBUS_TYPE_STRING, &enc_mode);
}
static void append_string_with_access_control(DBusMessageIter *dict,
struct connman_access_service_policy *policy, const char *name,
const char *str, enum connman_access default_access)
{
if (!str)
return;
if (connman_access_service_get_property(policy,
g_dbus_get_current_sender(), name,
default_access) != CONNMAN_ACCESS_ALLOW)
return;
connman_dbus_dict_append_basic(dict, name, DBUS_TYPE_STRING, &str);
}
static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
struct connman_service *service)
{
......@@ -2601,6 +2626,10 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
val = service->hidden_service;
connman_dbus_dict_append_basic(dict, "Hidden",
DBUS_TYPE_BOOLEAN, &val);
append_string_with_access_control(dict, service->policy,
"Passphrase", service->passphrase,
CONNMAN_ACCESS_DENY);
break;
case CONNMAN_SERVICE_TYPE_ETHERNET:
case CONNMAN_SERVICE_TYPE_BLUETOOTH:
......@@ -3285,6 +3314,23 @@ const char *__connman_service_get_passphrase(struct connman_service *service)
return service->passphrase;
}
void __connman_service_set_access(struct connman_service *service,
const char *access)
{
if (!service)
return;
if (!g_strcmp0(service->access, access))
return;
g_free(service->access);
connman_access_service_policy_free(service->policy);
/* NULL parameter initializes the default access policy */
service->policy = connman_access_service_policy_create(access);
service->access = g_strdup(access);
}
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
......@@ -3868,6 +3914,36 @@ static DBusMessage *set_property(DBusConnection *conn,
}
service_save(service);
} else if (g_str_equal(name, "Passphrase")) {
const char *str = NULL;
if (type != DBUS_TYPE_STRING)
return __connman_error_invalid_arguments(msg);
dbus_message_iter_get_basic(&value, &str);
if (connman_access_service_set_property(service->policy,
g_dbus_get_current_sender(), name,
CONNMAN_ACCESS_DENY) != CONNMAN_ACCESS_ALLOW)
return __connman_error_permission_denied(msg);
if (g_strcmp0(str, service->passphrase)) {
int err;
err = __connman_service_set_passphrase(service, str);
if (err) {
/*
* InvalidArguments is more appropriate here
* than PassphraseRequired
*/
if (err == -ENOKEY)
err = -EINVAL;
return __connman_error_failed(msg, -err);
}
service_save(service);
}
} else
return __connman_error_invalid_property(msg);
......@@ -5006,6 +5082,9 @@ static void service_destroy(struct connman_service *service)
stats_destroy(service);
connman_access_service_policy_free(service->policy);
g_free(service->access);
if (current_default == service)
current_default = NULL;
......@@ -5074,6 +5153,7 @@ static void service_initialize(struct connman_service *service)
service->online_check_timer_ipv4 = 0;
service->online_check_timer_ipv6 = 0;
service->policy = connman_access_service_policy_create(NULL);
}
/**
......
......@@ -6,7 +6,9 @@
#
# Tests with coverage enabled:
TESTS="test-ippool test-sailfish_wakeup_timer"
TESTS="test-access \
test-ippool \
test-sailfish_wakeup_timer"
pushd `dirname $0` > /dev/null
TEST_DIR="$PWD"
......
/*
* Connection Manager
*
* Copyright (C) 2017 Jolla Ltd. All rights reserved.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <connman/access.h>
#include <errno.h>
struct connman_access_service_policy_impl {
int unused;
};
static const struct connman_access_driver test_inval;
static struct connman_access_service_policy_impl *test_service_policy_create
(const char *spec)
{
return g_new0(struct connman_access_service_policy_impl, 1);
}
static void test_service_policy_free
(struct connman_access_service_policy_impl *policy)
{
g_free(policy);
}
/*==========================================================================*
* Test driver 1
*==========================================================================*/
static enum connman_access test1_service_get_property
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access)
{
return CONNMAN_ACCESS_ALLOW;
}
static enum connman_access test1_service_set_property
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access)
{
return CONNMAN_ACCESS_ALLOW;
}
static const struct connman_access_driver test1_driver = {
.name = "test1",
.default_service_policy = "allow",
.service_policy_create = test_service_policy_create,
.service_policy_free = test_service_policy_free,
.service_get_property = test1_service_get_property,
.service_set_property = test1_service_set_property
};
/*==========================================================================*
* Test driver 2
*==========================================================================*/
static enum connman_access test2_service_get_property
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access)
{
return CONNMAN_ACCESS_DENY;
}
static enum connman_access test2_service_set_property
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access)
{
return CONNMAN_ACCESS_DENY;
}
static const struct connman_access_driver test2_driver = {
.name = "test2",
.default_service_policy = "deny",
.service_policy_create = test_service_policy_create,
.service_policy_free = test_service_policy_free,
.service_get_property = test2_service_get_property,
.service_set_property = test2_service_set_property
};
/*==========================================================================*
* Test driver 3
*==========================================================================*/
static enum connman_access test3_service_get_property
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access)
{
return default_access;
}
static enum connman_access test3_service_set_property
(struct connman_access_service_policy_impl *policy,
const char *sender, const char *name,
enum connman_access default_access)
{
return default_access;
}
static const struct connman_access_driver test3_driver = {
.name = "test3",
.service_get_property = test3_service_get_property,
.service_set_property = test3_service_set_property
};
/*==========================================================================*
* Test driver 4
*==========================================================================*/
static struct connman_access_service_policy_impl *test4_service_policy_create
(const char *spec)
{
return NULL;
}
static const struct connman_access_driver test4_driver = {
.name = "test4",
.service_policy_create = test4_service_policy_create,
.service_get_property = test3_service_get_property,
.service_set_property = test3_service_set_property
};
/*==========================================================================*
* Test driver 5
*==========================================================================*/
static struct connman_access_service_policy_impl *test5_service_policy_create
(const char *spec)
{
static struct connman_access_service_policy_impl impl;
return &impl;
}
static const struct connman_access_driver test5_driver = {
.name = "test5",
.service_policy_create = test5_service_policy_create
};
/*==========================================================================*
* Tests
*==========================================================================*/
static void test_access_register()
{
g_assert(connman_access_driver_register(NULL) == -EINVAL);
g_assert(connman_access_driver_register(&test_inval) == -EINVAL);
g_assert(connman_access_driver_register(&test1_driver) == 0);
g_assert(connman_access_driver_register(&test1_driver) == -EALREADY);
connman_access_timer_unregister(&test1_driver);
connman_access_timer_unregister(&test1_driver);
connman_access_timer_unregister(NULL);
}
static void test_access_default_policy()
{
const char* def1 = "test1:allow";
const char* def2 = "test2:deny";
g_assert(!connman_access_default_service_policy());
g_assert(connman_access_driver_register(&test1_driver) == 0);
g_assert(!g_strcmp0(def1, connman_access_default_service_policy()));
g_assert(connman_access_driver_register(&test2_driver) == 0);
g_assert(!g_strcmp0(def2, connman_access_default_service_policy()));
g_assert(connman_access_driver_register(&test3_driver) == 0);
g_assert(!connman_access_default_service_policy());
connman_access_timer_unregister(&test3_driver);
g_assert(!g_strcmp0(def2, connman_access_default_service_policy()));
connman_access_timer_unregister(&test2_driver);
g_assert(!g_strcmp0(def1, connman_access_default_service_policy()));
connman_access_timer_unregister(&test1_driver);
g_assert(!connman_access_default_service_policy());
}
static void test_access_service_policy()
{
struct connman_access_service_policy *policy;
g_assert(!connman_access_service_policy_create(NULL));
g_assert(connman_access_driver_register(&test1_driver) == 0);
g_assert(connman_access_driver_register(&test2_driver) == 0);
/* test3_driver has no service_policy_create callback */
g_assert(connman_access_driver_register(&test3_driver) == 0);
g_assert(!connman_access_service_policy_create(NULL));
connman_access_timer_unregister(&test3_driver);
/* test4_driver has service_policy_create which returns NULL */
g_assert(connman_access_driver_register(&test4_driver) == 0);
g_assert(!connman_access_service_policy_create(NULL));
connman_access_timer_unregister(&test4_driver);
/*
* test5_driver has service_policy_create but no service_policy_free.
* It also has no service_get/set_property callbacks.