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>
35 #define CONNMAN_API_SUBJECT_TO_CHANGE
36 #include <connman/plugin.h>
37 #include <connman/log.h>
38 #include <connman/session.h>
39 #include <connman/dbus.h>
40 #include <connman/inotify.h>
42 #define POLICYDIR STORAGEDIR "/session_policy_local"
44 #define MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
45 S_IXGRP | S_IROTH | S_IXOTH)
47 static DBusConnection *connection;
49 static GHashTable *file_hash; /* (filename, policy_file) */
50 static GHashTable *session_hash; /* (connman_session, policy_config) */
52 /* Global lookup table for mapping sessions to policies */
53 static GHashTable *selinux_hash; /* (lsm context, policy_group) */
56 * A instance of struct policy_file is created per file in
61 * A valid file is a keyfile with one ore more groups. All
62 * groups are keept in this list.
71 * Each policy_group owns a config and is not shared with
72 * sessions. Instead each session copies the valued from this
75 struct connman_session_config *config;
77 /* All 'users' of this policy. */
81 /* A struct policy_config object is created and owned by a session. */
82 struct policy_config {
85 /* The policy config owned by the session */
86 struct connman_session_config *config;
88 /* To which policy belongs this policy_config */
89 struct connman_session *session;
91 * Points to the policy_group when a config has been applied
94 struct policy_group *group;
97 static void copy_session_config(struct connman_session_config *dst,
98 struct connman_session_config *src)
100 g_slist_free(dst->allowed_bearers);
101 dst->allowed_bearers = g_slist_copy(src->allowed_bearers);
102 dst->ecall = src->ecall;
103 dst->type = src->type;
104 dst->roaming_policy = src->roaming_policy;
105 dst->priority = src->priority;
108 static void set_policy(struct policy_config *policy,
109 struct policy_group *group)
111 DBG("policy %p group %p", policy, group);
113 group->sessions = g_slist_prepend(group->sessions, policy);
114 policy->group = group;
116 copy_session_config(policy->config, group->config);
119 static char *parse_selinux_type(const char *context)
121 char *ident, **tokens;
124 * SELinux combines Role-Based Access Control (RBAC), Type
125 * Enforcment (TE) and optionally Multi-Level Security (MLS).
127 * When SELinux is enabled all processes and files are labeled
128 * with a contex that contains information such as user, role
129 * type (and optionally a level). E.g.
132 * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
134 * For identifyng application we (ab)using the type
135 * information. In the above example the haifux_exec_t type
136 * will be transfered to haifux_t as defined in the domain
137 * transition and thus we are able to identify the application
141 tokens = g_strsplit(context, ":", 0);
142 if (g_strv_length(tokens) < 2) {
147 /* Use the SELinux type as identification token. */
148 ident = g_strdup(tokens[2]);
155 static void cleanup_config(gpointer user_data);
157 static void selinux_context_reply(const unsigned char *context, void *user_data,
160 struct cb_data *cbd = user_data;
161 connman_session_config_func_t cb = cbd->cb;
162 struct policy_config *policy = cbd->data;
163 struct policy_group *group;
164 struct connman_session_config *config = NULL;
167 DBG("session %p", policy->session);
172 DBG("SELinux context %s", context);
174 ident = parse_selinux_type((const char*)context);
180 policy->selinux = g_strdup(ident);
182 group = g_hash_table_lookup(selinux_hash, policy->selinux);
184 set_policy(policy, group);
186 g_hash_table_replace(session_hash, policy->session, policy);
187 config = policy->config;
190 (*cb)(policy->session, config, cbd->user_data, err);
193 cleanup_config(policy);
199 static int policy_local_create(struct connman_session *session,
200 connman_session_config_func_t cb,
203 struct cb_data *cbd = cb_data_new(cb, user_data);
204 struct policy_config *policy;
208 DBG("session %p", session);
210 policy = g_new0(struct policy_config, 1);
211 policy->config = connman_session_create_default_config();
212 policy->session = session;
216 owner = connman_session_get_owner(session);
218 err = connman_dbus_get_selinux_context(connection, owner,
219 selinux_context_reply,
222 connman_error("Could not get SELinux context");
223 cleanup_config(policy);
231 static void policy_local_destroy(struct connman_session *session)
233 struct policy_data *policy;
235 DBG("session %p", session);
237 policy = g_hash_table_lookup(session_hash, session);
241 g_hash_table_remove(session_hash, session);
244 static struct connman_session_policy session_policy_local = {
245 .name = "session local policy configuration",
246 .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
247 .create = policy_local_create,
248 .destroy = policy_local_destroy,
251 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
253 GError *error = NULL;
256 *keyfile = g_key_file_new();
258 if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE)
265 * The fancy G_FILE_ERROR_* codes are identical to the native
270 DBG("Unable to load %s: %s", pathname, error->message);
271 g_clear_error(&error);
273 g_key_file_free(*keyfile);
279 static int load_policy(GKeyFile *keyfile, const char *groupname,
280 struct policy_group *group)
282 struct connman_session_config *config = group->config;
286 group->selinux = g_key_file_get_string(keyfile, groupname,
288 if (group->selinux == NULL)
291 config->priority = g_key_file_get_boolean(keyfile, groupname,
294 str = g_key_file_get_string(keyfile, groupname, "RoamingPolicy",
297 config->roaming_policy = connman_session_parse_roaming_policy(str);
301 str = g_key_file_get_string(keyfile, groupname, "ConnectionType",
304 config->type = connman_session_parse_connection_type(str);
308 config->ecall = g_key_file_get_boolean(keyfile, groupname,
309 "EmergencyCall", NULL);
311 str = g_key_file_get_string(keyfile, groupname, "AllowedBearers",
314 tokens = g_strsplit(str, " ", 0);
316 for (i = 0; tokens[i] != NULL; i++) {
317 err = connman_session_parse_bearers(tokens[i],
318 &config->allowed_bearers);
327 DBG("group %p selinux %s", group, group->selinux);
331 static void update_session(struct policy_config *policy)
333 DBG("policy %p session %p", policy, policy->session);
335 if (policy->session == NULL)
338 if (connman_session_config_update(policy->session) < 0)
339 connman_session_destroy(policy->session);
342 static void set_default_config(gpointer user_data)
344 struct policy_config *policy = user_data;
346 connman_session_set_default_config(policy->config);
347 policy->group = NULL;
348 update_session(policy);
351 static void cleanup_config(gpointer user_data)
353 struct policy_config *policy = user_data;
355 DBG("policy %p group %p", policy, policy->group);
357 if (policy->group != NULL)
358 policy->group->sessions =
359 g_slist_remove(policy->group->sessions, policy);
361 g_slist_free(policy->config->allowed_bearers);
362 g_free(policy->config);
363 g_free(policy->selinux);
367 static void cleanup_group(gpointer user_data)
369 struct policy_group *group = user_data;
371 DBG("group %p", group);
373 g_slist_free_full(group->sessions, set_default_config);
375 g_slist_free(group->config->allowed_bearers);
376 g_free(group->config);
377 if (group->selinux != NULL)
378 g_hash_table_remove(selinux_hash, group->selinux);
379 g_free(group->selinux);
383 static void cleanup_file(gpointer user_data)
385 struct policy_file *file = user_data;
387 DBG("file %p", file);
389 g_slist_free_full(file->groups, cleanup_group);
393 static void recheck_sessions(void)
397 struct policy_group *group = NULL;
399 g_hash_table_iter_init(&iter, session_hash);
400 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
401 struct policy_config *policy = value;
403 if (policy->group != NULL)
406 group = g_hash_table_lookup(selinux_hash, policy->selinux);
408 set_policy(policy, group);
409 update_session(policy);
414 static int load_file(const char *filename, struct policy_file *file)
417 struct policy_group *group;
424 pathname = g_strdup_printf("%s/%s", POLICYDIR, filename);
425 err = load_keyfile(pathname, &keyfile);
431 groupnames = g_key_file_get_groups(keyfile, NULL);
433 for (i = 0; groupnames[i] != NULL; i++) {
434 group = g_new0(struct policy_group, 1);
435 group->config = g_new0(struct connman_session_config, 1);
437 err = load_policy(keyfile, groupnames[i], group);
439 g_free(group->config);
443 g_hash_table_replace(selinux_hash, group->selinux, group);
445 file->groups = g_slist_prepend(file->groups, group);
448 g_strfreev(groupnames);
451 g_slist_free_full(file->groups, cleanup_group);
453 g_key_file_free(keyfile);
458 static connman_bool_t is_filename_valid(const char *filename)
460 if (filename == NULL)
463 if (filename[0] == '.')
466 return g_str_has_suffix(filename, ".policy");
469 static int read_policies()
472 const gchar *filename;
473 struct policy_file *file;
477 dir = g_dir_open(POLICYDIR, 0, NULL);
481 while ((filename = g_dir_read_name(dir)) != NULL) {
482 if (is_filename_valid(filename) == FALSE)
485 file = g_new0(struct policy_file, 1);
486 if (load_file(filename, file) < 0) {
491 g_hash_table_replace(file_hash, g_strdup(filename), file);
500 static void notify_handler(struct inotify_event *event,
501 const char *filename)
503 struct policy_file *file;
505 DBG("event %x file %s", event->mask, filename);
507 if (event->mask & IN_CREATE)
510 if (is_filename_valid(filename) == FALSE)
514 * load_file() will modify the global selinux/uid/gid hash
515 * tables. We need to remove the old entries first before
516 * else the table points to the wrong entries.
518 g_hash_table_remove(file_hash, filename);
520 if (event->mask & (IN_DELETE | IN_MOVED_FROM))
523 if (event->mask & (IN_MOVED_TO | IN_MODIFY)) {
524 connman_info("Policy update for '%s'", filename);
526 file = g_new0(struct policy_file, 1);
527 if (load_file(filename, file) < 0) {
532 g_hash_table_replace(file_hash, g_strdup(filename), file);
537 static int session_policy_local_init(void)
543 /* If the dir doesn't exist, create it */
544 if (g_file_test(POLICYDIR, G_FILE_TEST_IS_DIR) == FALSE) {
545 if (mkdir(POLICYDIR, MODE) < 0) {
551 connection = connman_dbus_get_connection();
552 if (connection == NULL)
555 file_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
556 g_free, cleanup_file);
557 session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
558 NULL, cleanup_config);
559 selinux_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
562 err = connman_inotify_register(POLICYDIR, notify_handler);
566 err = connman_session_policy_register(&session_policy_local);
576 connman_inotify_unregister(POLICYDIR, notify_handler);
579 if (file_hash != NULL)
580 g_hash_table_destroy(file_hash);
582 if (session_hash != NULL)
583 g_hash_table_destroy(session_hash);
585 if (selinux_hash != NULL)
586 g_hash_table_destroy(selinux_hash);
588 connman_session_policy_unregister(&session_policy_local);
590 dbus_connection_unref(connection);
595 static void session_policy_local_exit(void)
599 g_hash_table_destroy(file_hash);
600 g_hash_table_destroy(session_hash);
601 g_hash_table_destroy(selinux_hash);
603 connman_session_policy_unregister(&session_policy_local);
605 dbus_connection_unref(connection);
607 connman_inotify_unregister(POLICYDIR, notify_handler);
610 CONNMAN_PLUGIN_DEFINE(session_policy_local,
611 "Session local file policy configuration plugin",
612 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
613 session_policy_local_init, session_policy_local_exit)