5 * Copyright (C) 2012 BMW Car IT GbmH. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <sys/inotify.h>
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/log.h>
37 #include <connman/session.h>
38 #include <connman/dbus.h>
39 #include <connman/inotify.h>
41 #define POLICYDIR STORAGEDIR "/session_policy_local"
43 static DBusConnection *connection;
45 static GHashTable *policy_hash;
46 static GHashTable *session_hash;
49 struct connman_session *session;
50 connman_session_config_cb callback;
58 struct connman_session *session;
59 struct connman_session_config *config;
62 static void cleanup_policy(gpointer user_data)
64 struct policy_data *policy = user_data;
66 if (policy->config != NULL)
67 g_slist_free(policy->config->allowed_bearers);
69 g_free(policy->ident);
70 g_free(policy->config);
74 static char *parse_ident(const unsigned char *context)
76 char *str, *ident, **tokens;
79 * SELinux combines Role-Based Access Control (RBAC), Type
80 * Enforcment (TE) and optionally Multi-Level Security (MLS).
82 * When SELinux is enabled all processes and files are labeled
83 * with a contex that contains information such as user, role
84 * type (and optionally a level). E.g.
87 * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
89 * For identifyng application we (ab)using the type
90 * information. In the above example the haifux_exec_t type
91 * will be transfered to haifux_t as defined in the domain
92 * transition and thus we are able to identify the application
96 str = g_strdup((const gchar*)context);
100 DBG("SELinux context %s", str);
102 tokens = g_strsplit(str, ":", 0);
103 if (tokens == NULL) {
108 /* Use the SELinux type as identification token. */
109 ident = g_strdup(tokens[2]);
117 static struct policy_data *create_policy(const char *ident)
119 struct policy_data *policy;
121 DBG("ident %s", ident);
123 policy = g_try_new0(struct policy_data, 1);
127 policy->config = connman_session_create_default_config();
128 if (policy->config == NULL) {
133 policy->refcount = 1;
134 policy->ident = g_strdup(ident);
136 g_hash_table_replace(policy_hash, policy->ident, policy);
141 static struct policy_data *policy_ref(struct policy_data *policy)
143 DBG("%p %s ref %d", policy, policy->ident, policy->refcount + 1);
145 __sync_fetch_and_add(&policy->refcount, 1);
150 static void policy_unref(struct policy_data *policy)
152 DBG(" %p %s ref %d", policy, policy->ident, policy->refcount - 1);
154 if (__sync_fetch_and_sub(&policy->refcount, 1) != 1)
157 g_hash_table_remove(policy_hash, policy->ident);
160 static void selinux_context_reply(const unsigned char *context, void *user_data,
163 struct create_data *data = user_data;
164 struct policy_data *policy;
165 struct connman_session_config *config = NULL;
168 DBG("session %p", data->session);
173 ident = parse_ident(context);
179 policy = g_hash_table_lookup(policy_hash, ident);
180 if (policy != NULL) {
182 policy->session = data->session;
184 policy = create_policy(ident);
185 if (policy == NULL) {
191 g_hash_table_replace(session_hash, data->session, policy);
192 config = policy->config;
195 (*data->callback)(data->session, config, data->user_data, err);
201 static int policy_local_create(struct connman_session *session,
202 connman_session_config_cb callback,
205 struct create_data *data;
209 DBG("session %p", session);
211 data = g_try_new0(struct create_data, 1);
215 data->session = session;
216 data->callback = callback;
217 data->user_data = user_data;
219 owner = connman_session_get_owner(session);
221 err = connman_dbus_get_selinux_context(connection, owner,
222 selinux_context_reply,
225 connman_error("Could not get SELinux context");
233 static void policy_local_destroy(struct connman_session *session)
235 struct policy_data *policy;
237 DBG("session %p", session);
239 policy = g_hash_table_lookup(session_hash, session);
240 g_hash_table_remove(session_hash, session);
241 policy->session = NULL;
243 policy_unref(policy);
246 static struct connman_session_policy session_policy_local = {
247 .name = "session local policy configuration",
248 .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
249 .create = policy_local_create,
250 .destroy = policy_local_destroy,
253 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
255 GError *error = NULL;
258 DBG("Loading %s", pathname);
260 *keyfile = g_key_file_new();
262 if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE)
269 * The fancy G_FILE_ERROR_* codes are identical to the native
274 DBG("Unable to load %s: %s", pathname, error->message);
275 g_clear_error(&error);
277 g_key_file_free(*keyfile);
283 static int load_policy(struct policy_data *policy)
285 struct connman_session_config *config = policy->config;
291 pathname = g_strdup_printf("%s/%s", POLICYDIR, policy->ident);
295 err = load_keyfile(pathname, &keyfile);
299 if (err == -ENOENT) {
300 /* Ignore empty files */
307 config->priority = g_key_file_get_boolean(keyfile, "Default",
310 str = g_key_file_get_string(keyfile, "Default", "RoamingPolicy",
313 config->roaming_policy = connman_session_parse_roaming_policy(str);
316 config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
319 str = g_key_file_get_string(keyfile, "Default", "ConnectionType",
322 config->type = connman_session_parse_connection_type(str);
325 config->type = CONNMAN_SESSION_TYPE_ANY;
328 config->ecall = g_key_file_get_boolean(keyfile, "Default",
329 "EmergencyCall", NULL);
331 g_slist_free(config->allowed_bearers);
332 config->allowed_bearers = NULL;
334 str = g_key_file_get_string(keyfile, "Default", "AllowedBearers",
338 tokens = g_strsplit(str, " ", 0);
340 for (i = 0; tokens[i] != NULL; i++) {
341 err = connman_session_parse_bearers(tokens[i],
342 &config->allowed_bearers);
350 config->allowed_bearers = g_slist_append(NULL,
351 GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
352 if (config->allowed_bearers == NULL)
356 g_key_file_free(keyfile);
362 static void update_session(struct connman_session *session)
364 if (connman_session_config_update(session) < 0)
365 connman_session_destroy(session);
368 static void remove_policy(struct policy_data *policy)
370 connman_bool_t update = FALSE;
373 if (policy->session != NULL)
376 policy_unref(policy);
381 err = connman_session_set_default_config(policy->config);
383 connman_session_destroy(policy->session);
387 update_session(policy->session);
390 static void notify_handler(struct inotify_event *event,
393 struct policy_data *policy;
398 policy = g_hash_table_lookup(policy_hash, ident);
400 if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
401 connman_info("Policy added for '%s'", ident);
406 policy = create_policy(ident);
412 if (event->mask & IN_MODIFY) {
413 connman_info("Policy modifed for '%s'", ident);
415 if (load_policy(policy) < 0) {
416 remove_policy(policy);
421 if (event->mask & (IN_DELETE | IN_MOVED_FROM)) {
422 connman_info("Policy deleted for '%s'", ident);
424 remove_policy(policy);
428 if (policy->session != NULL)
429 update_session(policy->session);
432 static int read_policies(void)
439 dir = g_dir_open(POLICYDIR, 0, NULL);
443 while ((file = g_dir_read_name(dir)) != NULL) {
444 struct policy_data *policy;
446 policy = create_policy(file);
447 if (policy == NULL) {
452 err = load_policy(policy);
463 static int session_policy_local_init(void)
467 connection = connman_dbus_get_connection();
468 if (connection == NULL)
471 session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
473 if (session_hash == NULL) {
478 policy_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
479 NULL, cleanup_policy);
480 if (policy_hash == NULL) {
485 err = connman_inotify_register(POLICYDIR, notify_handler);
489 err = read_policies();
493 err = connman_session_policy_register(&session_policy_local);
501 connman_inotify_unregister(POLICYDIR, notify_handler);
504 if (session_hash != NULL)
505 g_hash_table_destroy(session_hash);
506 if (policy_hash != NULL)
507 g_hash_table_destroy(policy_hash);
509 connman_session_policy_unregister(&session_policy_local);
511 dbus_connection_unref(connection);
516 static void session_policy_local_exit(void)
518 g_hash_table_destroy(session_hash);
519 g_hash_table_destroy(policy_hash);
521 connman_session_policy_unregister(&session_policy_local);
523 dbus_connection_unref(connection);
525 connman_inotify_unregister(POLICYDIR, notify_handler);
528 CONNMAN_PLUGIN_DEFINE(session_policy_local,
529 "Session local file policy configuration plugin",
530 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
531 session_policy_local_init, session_policy_local_exit)