5 * Copyright (C) 2012-2014 BMW Car IT GmbH.
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>
30 #include <sys/types.h>
38 #define CONNMAN_API_SUBJECT_TO_CHANGE
39 #include <connman/plugin.h>
40 #include <connman/log.h>
41 #include <connman/session.h>
42 #include <connman/dbus.h>
43 #include <connman/inotify.h>
45 #include "src/shared/util.h"
47 #define POLICYDIR STORAGEDIR "/session_policy_local"
49 #define MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
50 S_IXGRP | S_IROTH | S_IXOTH)
52 static DBusConnection *connection;
54 static GHashTable *file_hash; /* (filename, policy_file) */
55 static GHashTable *session_hash; /* (connman_session, policy_config) */
57 /* Global lookup tables for mapping sessions to policies */
58 static GHashTable *selinux_hash; /* (lsm context, policy_group) */
59 static GHashTable *uid_hash; /* (uid, policy_group) */
60 static GHashTable *gid_hash; /* (gid, policy_group) */
63 * A instance of struct policy_file is created per file in
68 * A valid file is a keyfile with one ore more groups. All
69 * groups are keept in this list.
80 * Each policy_group owns a config and is not shared with
81 * sessions. Instead each session copies the valued from this
84 struct connman_session_config *config;
86 /* All 'users' of this policy. */
90 /* A struct policy_config object is created and owned by a session. */
91 struct policy_config {
93 char *selinux_context;
97 /* The policy config owned by the session */
98 struct connman_session_config *config;
100 /* To which policy belongs this policy_config */
101 struct connman_session *session;
103 * Points to the policy_group when a config has been applied
106 struct policy_group *group;
109 static void copy_session_config(struct connman_session_config *dst,
110 struct connman_session_config *src)
112 g_slist_free(dst->allowed_bearers);
113 dst->allowed_bearers = g_slist_copy(src->allowed_bearers);
114 dst->ecall = src->ecall;
115 dst->type = src->type;
116 dst->roaming_policy = src->roaming_policy;
117 dst->priority = src->priority;
120 static void set_policy(struct policy_config *policy,
121 struct policy_group *group)
123 DBG("policy %p group %p", policy, group);
125 group->sessions = g_slist_prepend(group->sessions, policy);
126 policy->group = group;
128 copy_session_config(policy->config, group->config);
131 static char *parse_selinux_type(const char *context)
133 char *ident, **tokens;
136 * SELinux combines Role-Based Access Control (RBAC), Type
137 * Enforcment (TE) and optionally Multi-Level Security (MLS).
139 * When SELinux is enabled all processes and files are labeled
140 * with a contex that contains information such as user, role
141 * type (and optionally a level). E.g.
144 * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
146 * For identifyng application we (ab)using the type
147 * information. In the above example the haifux_exec_t type
148 * will be transfered to haifux_t as defined in the domain
149 * transition and thus we are able to identify the application
153 tokens = g_strsplit(context, ":", 0);
154 if (g_strv_length(tokens) < 2) {
159 /* Use the SELinux type as identification token. */
160 ident = g_strdup(tokens[2]);
167 static void cleanup_config(gpointer user_data);
169 static void finish_create(struct policy_config *policy,
170 connman_session_config_func_t cb,
173 struct policy_group *group = NULL;
177 group = g_hash_table_lookup(selinux_hash, policy->selinux);
180 set_policy(policy, group);
182 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_LSM;
183 policy->config->id = g_strdup(policy->selinux_context);
188 group = g_hash_table_lookup(uid_hash, policy->uid);
191 set_policy(policy, group);
193 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_UID;
194 policy->config->id = g_strdup(policy->uid);
198 for (list = policy->gids; list; list = list->next) {
199 char *gid = list->data;
201 group = g_hash_table_lookup(gid_hash, gid);
205 set_policy(policy, group);
207 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_GID;
208 policy->config->id = g_strdup(gid);
212 g_hash_table_replace(session_hash, policy->session, policy);
214 (*cb)(policy->session, policy->config, user_data, 0);
217 static void failed_create(struct policy_config *policy,
218 connman_session_config_func_t cb,
219 void *user_data, int err)
221 (*cb)(policy->session, NULL, user_data, err);
223 cleanup_config(policy);
226 static void selinux_context_reply(const unsigned char *context, void *user_data,
229 struct cb_data *cbd = user_data;
230 connman_session_config_func_t cb = cbd->cb;
231 struct policy_config *policy = cbd->data;
234 DBG("session %p", policy->session);
237 /* No SELinux support, drop back to UID/GID only mode */
238 finish_create(policy, cb, cbd->user_data);
243 failed_create(policy, cb, cbd->user_data, err);
247 DBG("SELinux context %s", context);
249 policy->selinux_context = g_strdup((const char *)context);
250 ident = parse_selinux_type(policy->selinux_context);
252 policy->selinux = g_strdup(ident);
254 finish_create(policy, cb, cbd->user_data);
261 static void get_uid_reply(unsigned int uid, void *user_data, int err)
263 struct cb_data *cbd = user_data;
264 connman_session_config_func_t cb = cbd->cb;
265 struct policy_config *policy = cbd->data;
269 gid_t *groups = NULL;
272 DBG("session %p uid %d", policy->session, uid);
275 cleanup_config(policy);
279 pwd = getpwuid((uid_t)uid);
288 policy->uid = g_strdup(pwd->pw_name);
291 getgrouplist(pwd->pw_name, pwd->pw_gid, NULL, &nrgroups);
292 groups = g_try_new0(gid_t, nrgroups);
298 err = getgrouplist(pwd->pw_name, pwd->pw_gid, groups, &nrgroups);
302 for (i = 0; i < nrgroups; i++) {
303 grp = getgrgid(groups[i]);
312 policy->gids = g_slist_prepend(policy->gids,
313 g_strdup(grp->gr_name));
317 owner = connman_session_get_owner(policy->session);
319 err = connman_dbus_get_selinux_context(connection, owner,
320 selinux_context_reply, cbd);
323 * We are able to ask for a SELinux context. Let's defer the
324 * creation of the session config until we get the answer
330 finish_create(policy, cb, cbd->user_data);
336 failed_create(NULL, cb, cbd->user_data, err);
341 static int policy_local_create(struct connman_session *session,
342 connman_session_config_func_t cb,
345 struct cb_data *cbd = cb_data_new(cb, user_data);
346 struct policy_config *policy;
350 DBG("session %p", session);
352 policy = g_new0(struct policy_config, 1);
353 policy->config = connman_session_create_default_config();
354 policy->session = session;
358 owner = connman_session_get_owner(session);
360 err = connman_dbus_get_connection_unix_user(connection, owner,
363 connman_error("Could not get UID");
364 cleanup_config(policy);
372 static void policy_local_destroy(struct connman_session *session)
374 struct policy_data *policy;
376 DBG("session %p", session);
378 policy = g_hash_table_lookup(session_hash, session);
382 g_hash_table_remove(session_hash, session);
385 static struct connman_session_policy session_policy_local = {
386 .name = "session local policy configuration",
387 .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
388 .create = policy_local_create,
389 .destroy = policy_local_destroy,
392 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
394 GError *error = NULL;
397 *keyfile = g_key_file_new();
399 if (!g_key_file_load_from_file(*keyfile, pathname, 0, &error))
406 * The fancy G_FILE_ERROR_* codes are identical to the native
411 DBG("Unable to load %s: %s", pathname, error->message);
412 g_clear_error(&error);
414 g_key_file_free(*keyfile);
420 static int load_policy(GKeyFile *keyfile, const char *groupname,
421 struct policy_group *group)
423 struct connman_session_config *config = group->config;
427 group->selinux = g_key_file_get_string(keyfile, groupname,
430 group->gid = g_key_file_get_string(keyfile, groupname,
433 group->uid = g_key_file_get_string(keyfile, groupname,
436 if (!group->selinux && !group->gid && !group->uid)
439 config->priority = g_key_file_get_boolean(keyfile, groupname,
442 str = g_key_file_get_string(keyfile, groupname, "RoamingPolicy",
445 config->roaming_policy = connman_session_parse_roaming_policy(str);
449 str = g_key_file_get_string(keyfile, groupname, "ConnectionType",
452 config->type = connman_session_parse_connection_type(str);
456 config->ecall = g_key_file_get_boolean(keyfile, groupname,
457 "EmergencyCall", NULL);
459 str = g_key_file_get_string(keyfile, groupname, "AllowedBearers",
462 g_slist_free(config->allowed_bearers);
463 config->allowed_bearers = NULL;
464 tokens = g_strsplit(str, " ", 0);
466 for (i = 0; tokens[i]; i++) {
467 err = connman_session_parse_bearers(tokens[i],
468 &config->allowed_bearers);
477 DBG("group %p selinux %s uid %s gid %s", group, group->selinux,
478 group->uid, group->gid);
483 static void update_session(struct policy_config *policy)
485 DBG("policy %p session %p", policy, policy->session);
487 if (!policy->session)
490 if (connman_session_config_update(policy->session) < 0)
491 connman_session_destroy(policy->session);
494 static void set_default_config(gpointer user_data)
496 struct policy_config *policy = user_data;
498 connman_session_set_default_config(policy->config);
499 policy->group = NULL;
500 update_session(policy);
503 static void cleanup_config(gpointer user_data)
505 struct policy_config *policy = user_data;
507 DBG("policy %p group %p", policy, policy->group);
510 policy->group->sessions =
511 g_slist_remove(policy->group->sessions, policy);
513 g_slist_free(policy->config->allowed_bearers);
514 g_free(policy->config->id);
515 g_free(policy->config);
516 g_free(policy->selinux_context);
517 g_free(policy->selinux);
519 g_slist_free_full(policy->gids, g_free);
523 static void cleanup_group(gpointer user_data)
525 struct policy_group *group = user_data;
527 DBG("group %p", group);
529 g_slist_free_full(group->sessions, set_default_config);
531 g_slist_free(group->config->allowed_bearers);
532 g_free(group->config->id);
533 g_free(group->config);
535 g_hash_table_remove(selinux_hash, group->selinux);
537 g_hash_table_remove(uid_hash, group->uid);
539 g_hash_table_remove(gid_hash, group->gid);
540 g_free(group->selinux);
546 static void cleanup_file(gpointer user_data)
548 struct policy_file *file = user_data;
550 DBG("file %p", file);
552 g_slist_free_full(file->groups, cleanup_group);
556 static void recheck_sessions(void)
560 struct policy_group *group = NULL;
563 g_hash_table_iter_init(&iter, session_hash);
564 while (g_hash_table_iter_next(&iter, &key, &value)) {
565 struct policy_config *policy = value;
571 group = g_hash_table_lookup(selinux_hash,
574 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_LSM;
575 g_free(policy->config->id);
576 policy->config->id = g_strdup(policy->selinux_context);
577 update_session(policy);
581 group = g_hash_table_lookup(uid_hash, policy->uid);
583 set_policy(policy, group);
585 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_UID;
586 g_free(policy->config->id);
587 policy->config->id = g_strdup(policy->uid);
588 update_session(policy);
592 for (list = policy->gids; list; list = list->next) {
593 char *gid = list->data;
594 group = g_hash_table_lookup(gid_hash, gid);
596 set_policy(policy, group);
598 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_GID;
599 g_free(policy->config->id);
600 policy->config->id = g_strdup(gid);
601 update_session(policy);
607 static int load_file(const char *filename, struct policy_file *file)
610 struct policy_group *group;
617 pathname = g_strdup_printf("%s/%s", POLICYDIR, filename);
618 err = load_keyfile(pathname, &keyfile);
624 groupnames = g_key_file_get_groups(keyfile, NULL);
626 for (i = 0; groupnames[i]; i++) {
627 group = g_new0(struct policy_group, 1);
628 group->config = connman_session_create_default_config();
630 err = load_policy(keyfile, groupnames[i], group);
632 g_free(group->config);
637 g_hash_table_replace(selinux_hash, group->selinux, group);
640 g_hash_table_replace(uid_hash, group->uid, group);
643 g_hash_table_replace(gid_hash, group->gid, group);
645 file->groups = g_slist_prepend(file->groups, group);
648 g_strfreev(groupnames);
651 g_slist_free_full(file->groups, cleanup_group);
653 g_key_file_free(keyfile);
658 static bool is_filename_valid(const char *filename)
663 if (filename[0] == '.')
666 return g_str_has_suffix(filename, ".policy");
669 static int read_policies(void)
672 const gchar *filename;
673 struct policy_file *file;
677 dir = g_dir_open(POLICYDIR, 0, NULL);
681 while ((filename = g_dir_read_name(dir))) {
682 if (!is_filename_valid(filename))
685 file = g_new0(struct policy_file, 1);
686 if (load_file(filename, file) < 0) {
691 g_hash_table_replace(file_hash, g_strdup(filename), file);
700 static void notify_handler(struct inotify_event *event,
701 const char *filename)
703 struct policy_file *file;
705 DBG("event %x file %s", event->mask, filename);
707 if (event->mask & IN_CREATE)
710 if (!is_filename_valid(filename))
714 * load_file() will modify the global selinux/uid/gid hash
715 * tables. We need to remove the old entries first before
716 * else the table points to the wrong entries.
718 g_hash_table_remove(file_hash, filename);
720 if (event->mask & (IN_DELETE | IN_MOVED_FROM))
723 if (event->mask & (IN_MOVED_TO | IN_MODIFY)) {
724 connman_info("Policy update for '%s'", filename);
726 file = g_new0(struct policy_file, 1);
727 if (load_file(filename, file) < 0) {
732 g_hash_table_replace(file_hash, g_strdup(filename), file);
737 static int session_policy_local_init(void)
743 /* If the dir doesn't exist, create it */
744 if (!g_file_test(POLICYDIR, G_FILE_TEST_IS_DIR)) {
745 if (mkdir(POLICYDIR, MODE) < 0) {
751 connection = connman_dbus_get_connection();
755 file_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
756 g_free, cleanup_file);
757 session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
758 NULL, cleanup_config);
759 selinux_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
761 uid_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
763 gid_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
766 err = connman_inotify_register(POLICYDIR, notify_handler);
770 err = connman_session_policy_register(&session_policy_local);
780 connman_inotify_unregister(POLICYDIR, notify_handler);
784 g_hash_table_destroy(file_hash);
787 g_hash_table_destroy(session_hash);
790 g_hash_table_destroy(selinux_hash);
793 g_hash_table_destroy(uid_hash);
796 g_hash_table_destroy(gid_hash);
798 connman_session_policy_unregister(&session_policy_local);
800 dbus_connection_unref(connection);
805 static void session_policy_local_exit(void)
809 g_hash_table_destroy(file_hash);
810 g_hash_table_destroy(session_hash);
811 g_hash_table_destroy(selinux_hash);
812 g_hash_table_destroy(uid_hash);
813 g_hash_table_destroy(gid_hash);
815 connman_session_policy_unregister(&session_policy_local);
817 dbus_connection_unref(connection);
819 connman_inotify_unregister(POLICYDIR, notify_handler);
822 CONNMAN_PLUGIN_DEFINE(session_policy_local,
823 "Session local file policy configuration plugin",
824 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
825 session_policy_local_init, session_policy_local_exit)