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 struct connman_session *session;
60 * A instance of struct policy_file is created per file in
65 * A valid file is a keyfile with one ore more groups. All
66 * groups are keept in this list.
75 * Each policy_group owns a config and is not shared with
76 * sessions. Instead each session copies the valued from this
79 struct connman_session_config *config;
81 /* All 'users' of this policy. */
85 /* A struct policy_config object is created and owned by a session. */
86 struct policy_config {
89 /* The policy config owned by the session */
90 struct connman_session_config *config;
92 /* To which policy belongs this policy_config */
93 struct connman_session *session;
95 * Points to the policy_group when a config has been applied
98 struct policy_group *group;
101 static void copy_session_config(struct connman_session_config *dst,
102 struct connman_session_config *src)
104 g_slist_free(dst->allowed_bearers);
105 dst->allowed_bearers = g_slist_copy(src->allowed_bearers);
106 dst->ecall = src->ecall;
107 dst->type = src->type;
108 dst->roaming_policy = src->roaming_policy;
109 dst->priority = src->priority;
112 static void set_policy(struct policy_config *policy,
113 struct policy_group *group)
115 DBG("policy %p group %p", policy, group);
117 group->sessions = g_slist_prepend(group->sessions, policy);
118 policy->group = group;
120 copy_session_config(policy->config, group->config);
123 static char *parse_selinux_type(const char *context)
125 char *ident, **tokens;
128 * SELinux combines Role-Based Access Control (RBAC), Type
129 * Enforcment (TE) and optionally Multi-Level Security (MLS).
131 * When SELinux is enabled all processes and files are labeled
132 * with a contex that contains information such as user, role
133 * type (and optionally a level). E.g.
136 * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
138 * For identifyng application we (ab)using the type
139 * information. In the above example the haifux_exec_t type
140 * will be transfered to haifux_t as defined in the domain
141 * transition and thus we are able to identify the application
145 tokens = g_strsplit(context, ":", 0);
146 if (g_strv_length(tokens) < 2) {
151 /* Use the SELinux type as identification token. */
152 ident = g_strdup(tokens[2]);
159 static struct policy_config *create_policy(void)
161 struct policy_config *policy;
163 policy = g_new0(struct policy_config, 1);
165 DBG("policy %p", policy);
167 policy->config = connman_session_create_default_config();
172 static void selinux_context_reply(const unsigned char *context, void *user_data,
175 struct cb_data *cbd = user_data;
176 connman_session_config_func_t cb = cbd->cb;
177 struct create_data *data = cbd->data;
178 struct policy_config *policy;
179 struct policy_group *group;
180 struct connman_session_config *config = NULL;
183 DBG("session %p", data->session);
188 DBG("SELinux context %s", context);
190 ident = parse_selinux_type((const char*)context);
196 policy = create_policy();
197 policy->selinux = g_strdup(ident);
198 policy->session = data->session;
200 group = g_hash_table_lookup(selinux_hash, policy->selinux);
202 set_policy(policy, group);
204 g_hash_table_replace(session_hash, data->session, policy);
205 config = policy->config;
208 (*cb)(data->session, config, cbd->user_data, err);
215 static int policy_local_create(struct connman_session *session,
216 connman_session_config_func_t cb,
219 struct cb_data *cbd = cb_data_new(cb, user_data);
220 struct create_data *data;
224 DBG("session %p", session);
226 data = g_new0(struct create_data, 1);
229 data->session = session;
231 owner = connman_session_get_owner(session);
233 err = connman_dbus_get_selinux_context(connection, owner,
234 selinux_context_reply,
237 connman_error("Could not get SELinux context");
246 static void policy_local_destroy(struct connman_session *session)
248 struct policy_data *policy;
250 DBG("session %p", session);
252 policy = g_hash_table_lookup(session_hash, session);
256 g_hash_table_remove(session_hash, session);
259 static struct connman_session_policy session_policy_local = {
260 .name = "session local policy configuration",
261 .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
262 .create = policy_local_create,
263 .destroy = policy_local_destroy,
266 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
268 GError *error = NULL;
271 *keyfile = g_key_file_new();
273 if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE)
280 * The fancy G_FILE_ERROR_* codes are identical to the native
285 DBG("Unable to load %s: %s", pathname, error->message);
286 g_clear_error(&error);
288 g_key_file_free(*keyfile);
294 static int load_policy(GKeyFile *keyfile, const char *groupname,
295 struct policy_group *group)
297 struct connman_session_config *config = group->config;
301 group->selinux = g_key_file_get_string(keyfile, groupname,
303 if (group->selinux == NULL)
306 config->priority = g_key_file_get_boolean(keyfile, groupname,
309 str = g_key_file_get_string(keyfile, groupname, "RoamingPolicy",
312 config->roaming_policy = connman_session_parse_roaming_policy(str);
316 str = g_key_file_get_string(keyfile, groupname, "ConnectionType",
319 config->type = connman_session_parse_connection_type(str);
323 config->ecall = g_key_file_get_boolean(keyfile, groupname,
324 "EmergencyCall", NULL);
326 str = g_key_file_get_string(keyfile, groupname, "AllowedBearers",
329 tokens = g_strsplit(str, " ", 0);
331 for (i = 0; tokens[i] != NULL; i++) {
332 err = connman_session_parse_bearers(tokens[i],
333 &config->allowed_bearers);
342 DBG("group %p selinux %s", group, group->selinux);
346 static void update_session(struct policy_config *policy)
348 DBG("policy %p session %p", policy, policy->session);
350 if (policy->session == NULL)
353 if (connman_session_config_update(policy->session) < 0)
354 connman_session_destroy(policy->session);
357 static void set_default_config(gpointer user_data)
359 struct policy_config *policy = user_data;
361 connman_session_set_default_config(policy->config);
362 policy->group = NULL;
363 update_session(policy);
366 static void cleanup_config(gpointer user_data)
368 struct policy_config *policy = user_data;
370 DBG("policy %p group %p", policy, policy->group);
372 if (policy->group != NULL)
373 policy->group->sessions =
374 g_slist_remove(policy->group->sessions, policy);
376 g_slist_free(policy->config->allowed_bearers);
377 g_free(policy->config);
378 g_free(policy->selinux);
382 static void cleanup_group(gpointer user_data)
384 struct policy_group *group = user_data;
386 DBG("group %p", group);
388 g_slist_free_full(group->sessions, set_default_config);
390 g_slist_free(group->config->allowed_bearers);
391 g_free(group->config);
392 if (group->selinux != NULL)
393 g_hash_table_remove(selinux_hash, group->selinux);
394 g_free(group->selinux);
398 static void cleanup_file(gpointer user_data)
400 struct policy_file *file = user_data;
402 DBG("file %p", file);
404 g_slist_free_full(file->groups, cleanup_group);
408 static void recheck_sessions(void)
412 struct policy_group *group = NULL;
414 g_hash_table_iter_init(&iter, session_hash);
415 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
416 struct policy_config *policy = value;
418 if (policy->group != NULL)
421 group = g_hash_table_lookup(selinux_hash, policy->selinux);
423 set_policy(policy, group);
424 update_session(policy);
429 static int load_file(const char *filename, struct policy_file *file)
432 struct policy_group *group;
439 pathname = g_strdup_printf("%s/%s", POLICYDIR, filename);
440 err = load_keyfile(pathname, &keyfile);
446 groupnames = g_key_file_get_groups(keyfile, NULL);
448 for (i = 0; groupnames[i] != NULL; i++) {
449 group = g_new0(struct policy_group, 1);
450 group->config = g_new0(struct connman_session_config, 1);
452 err = load_policy(keyfile, groupnames[i], group);
454 g_free(group->config);
458 g_hash_table_replace(selinux_hash, group->selinux, group);
460 file->groups = g_slist_prepend(file->groups, group);
463 g_strfreev(groupnames);
466 g_slist_free_full(file->groups, cleanup_group);
468 g_key_file_free(keyfile);
473 static connman_bool_t is_filename_valid(const char *filename)
475 if (filename == NULL)
478 if (filename[0] == '.')
481 return g_str_has_suffix(filename, ".policy");
484 static int read_policies()
487 const gchar *filename;
488 struct policy_file *file;
492 dir = g_dir_open(POLICYDIR, 0, NULL);
496 while ((filename = g_dir_read_name(dir)) != NULL) {
497 if (is_filename_valid(filename) == FALSE)
500 file = g_new0(struct policy_file, 1);
501 if (load_file(filename, file) < 0) {
506 g_hash_table_replace(file_hash, g_strdup(filename), file);
515 static void notify_handler(struct inotify_event *event,
516 const char *filename)
518 struct policy_file *file;
520 DBG("event %x file %s", event->mask, filename);
522 if (event->mask & IN_CREATE)
525 if (is_filename_valid(filename) == FALSE)
529 * load_file() will modify the global selinux/uid/gid hash
530 * tables. We need to remove the old entries first before
531 * else the table points to the wrong entries.
533 g_hash_table_remove(file_hash, filename);
535 if (event->mask & (IN_DELETE | IN_MOVED_FROM))
538 if (event->mask & (IN_MOVED_TO | IN_MODIFY)) {
539 connman_info("Policy update for '%s'", filename);
541 file = g_new0(struct policy_file, 1);
542 if (load_file(filename, file) < 0) {
547 g_hash_table_replace(file_hash, g_strdup(filename), file);
552 static int session_policy_local_init(void)
558 /* If the dir doesn't exist, create it */
559 if (g_file_test(POLICYDIR, G_FILE_TEST_IS_DIR) == FALSE) {
560 if (mkdir(POLICYDIR, MODE) < 0) {
566 connection = connman_dbus_get_connection();
567 if (connection == NULL)
570 file_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
571 g_free, cleanup_file);
572 session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
573 NULL, cleanup_config);
574 selinux_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
577 err = connman_inotify_register(POLICYDIR, notify_handler);
581 err = connman_session_policy_register(&session_policy_local);
591 connman_inotify_unregister(POLICYDIR, notify_handler);
594 if (file_hash != NULL)
595 g_hash_table_destroy(file_hash);
597 if (session_hash != NULL)
598 g_hash_table_destroy(session_hash);
600 if (selinux_hash != NULL)
601 g_hash_table_destroy(selinux_hash);
603 connman_session_policy_unregister(&session_policy_local);
605 dbus_connection_unref(connection);
610 static void session_policy_local_exit(void)
614 g_hash_table_destroy(file_hash);
615 g_hash_table_destroy(session_hash);
616 g_hash_table_destroy(selinux_hash);
618 connman_session_policy_unregister(&session_policy_local);
620 dbus_connection_unref(connection);
622 connman_inotify_unregister(POLICYDIR, notify_handler);
625 CONNMAN_PLUGIN_DEFINE(session_policy_local,
626 "Session local file policy configuration plugin",
627 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
628 session_policy_local_init, session_policy_local_exit)