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>
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 #define POLICYDIR STORAGEDIR "/session_policy_local"
47 #define MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
48 S_IXGRP | S_IROTH | S_IXOTH)
50 static DBusConnection *connection;
52 static GHashTable *file_hash; /* (filename, policy_file) */
53 static GHashTable *session_hash; /* (connman_session, policy_config) */
55 /* Global lookup tables for mapping sessions to policies */
56 static GHashTable *selinux_hash; /* (lsm context, policy_group) */
57 static GHashTable *uid_hash; /* (uid, policy_group) */
58 static GHashTable *gid_hash; /* (gid, policy_group) */
61 * A instance of struct policy_file is created per file in
66 * A valid file is a keyfile with one ore more groups. All
67 * groups are keept in this list.
78 * Each policy_group owns a config and is not shared with
79 * sessions. Instead each session copies the valued from this
82 struct connman_session_config *config;
84 /* All 'users' of this policy. */
88 /* A struct policy_config object is created and owned by a session. */
89 struct policy_config {
91 char *selinux_context;
95 /* The policy config owned by the session */
96 struct connman_session_config *config;
98 /* To which policy belongs this policy_config */
99 struct connman_session *session;
101 * Points to the policy_group when a config has been applied
104 struct policy_group *group;
107 static void copy_session_config(struct connman_session_config *dst,
108 struct connman_session_config *src)
110 g_slist_free(dst->allowed_bearers);
111 dst->allowed_bearers = g_slist_copy(src->allowed_bearers);
112 dst->ecall = src->ecall;
113 dst->type = src->type;
114 dst->roaming_policy = src->roaming_policy;
115 dst->priority = src->priority;
118 static void set_policy(struct policy_config *policy,
119 struct policy_group *group)
121 DBG("policy %p group %p", policy, group);
123 group->sessions = g_slist_prepend(group->sessions, policy);
124 policy->group = group;
126 copy_session_config(policy->config, group->config);
129 static char *parse_selinux_type(const char *context)
131 char *ident, **tokens;
134 * SELinux combines Role-Based Access Control (RBAC), Type
135 * Enforcment (TE) and optionally Multi-Level Security (MLS).
137 * When SELinux is enabled all processes and files are labeled
138 * with a contex that contains information such as user, role
139 * type (and optionally a level). E.g.
142 * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
144 * For identifyng application we (ab)using the type
145 * information. In the above example the haifux_exec_t type
146 * will be transfered to haifux_t as defined in the domain
147 * transition and thus we are able to identify the application
151 tokens = g_strsplit(context, ":", 0);
152 if (g_strv_length(tokens) < 2) {
157 /* Use the SELinux type as identification token. */
158 ident = g_strdup(tokens[2]);
165 static void cleanup_config(gpointer user_data);
167 static void finish_create(struct policy_config *policy,
168 connman_session_config_func_t cb,
171 struct policy_group *group;
174 group = g_hash_table_lookup(selinux_hash, policy->selinux);
176 set_policy(policy, group);
178 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_LSM;
179 policy->config->id = g_strdup(policy->selinux_context);
183 group = g_hash_table_lookup(uid_hash, policy->uid);
185 set_policy(policy, group);
187 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_UID;
188 policy->config->id = g_strdup(policy->uid);
192 for (list = policy->gids; list != NULL; list = list->next) {
193 char *gid = list->data;
195 group = g_hash_table_lookup(gid_hash, gid);
199 set_policy(policy, group);
201 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_GID;
202 policy->config->id = g_strdup(gid);
206 g_hash_table_replace(session_hash, policy->session, policy);
208 (*cb)(policy->session, policy->config, user_data, 0);
211 static void failed_create(struct policy_config *policy,
212 connman_session_config_func_t cb,
213 void *user_data, int err)
215 (*cb)(policy->session, NULL, user_data, err);
217 cleanup_config(policy);
220 static void selinux_context_reply(const unsigned char *context, void *user_data,
223 struct cb_data *cbd = user_data;
224 connman_session_config_func_t cb = cbd->cb;
225 struct policy_config *policy = cbd->data;
228 DBG("session %p", policy->session);
231 failed_create(policy, cb, user_data, err);
235 DBG("SELinux context %s", context);
237 policy->selinux_context = g_strdup((const char *)context);
238 ident = parse_selinux_type(policy->selinux_context);
240 policy->selinux = g_strdup(ident);
242 finish_create(policy, cb, cbd->user_data);
249 static void get_uid_reply(unsigned int uid, void *user_data, int err)
251 struct cb_data *cbd = user_data;
252 connman_session_config_func_t cb = cbd->cb;
253 struct policy_config *policy = cbd->data;
257 gid_t *groups = NULL;
260 DBG("session %p uid %d", policy->session, uid);
263 cleanup_config(policy);
267 pwd = getpwuid((uid_t)uid);
276 policy->uid = g_strdup(pwd->pw_name);
279 getgrouplist(pwd->pw_name, pwd->pw_gid, NULL, &nrgroups);
280 groups = g_try_new0(gid_t, nrgroups);
281 if (groups == NULL) {
286 err = getgrouplist(pwd->pw_name, pwd->pw_gid, groups, &nrgroups);
290 for (i = 0; i < nrgroups; i++) {
291 grp = getgrgid(groups[i]);
300 policy->gids = g_slist_prepend(policy->gids,
301 g_strdup(grp->gr_name));
305 owner = connman_session_get_owner(policy->session);
307 err = connman_dbus_get_selinux_context(connection, owner,
308 selinux_context_reply, cbd);
311 * We are able to ask for a SELinux context. Let's defer the
312 * creation of the session config until we get the answer
318 finish_create(policy, cb, cbd->user_data);
324 failed_create(NULL, cb, user_data, err);
329 static int policy_local_create(struct connman_session *session,
330 connman_session_config_func_t cb,
333 struct cb_data *cbd = cb_data_new(cb, user_data);
334 struct policy_config *policy;
338 DBG("session %p", session);
340 policy = g_new0(struct policy_config, 1);
341 policy->config = connman_session_create_default_config();
342 policy->session = session;
346 owner = connman_session_get_owner(session);
348 err = connman_dbus_get_connection_unix_user(connection, owner,
351 connman_error("Could not get UID");
352 cleanup_config(policy);
360 static void policy_local_destroy(struct connman_session *session)
362 struct policy_data *policy;
364 DBG("session %p", session);
366 policy = g_hash_table_lookup(session_hash, session);
370 g_hash_table_remove(session_hash, session);
373 static struct connman_session_policy session_policy_local = {
374 .name = "session local policy configuration",
375 .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
376 .create = policy_local_create,
377 .destroy = policy_local_destroy,
380 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
382 GError *error = NULL;
385 *keyfile = g_key_file_new();
387 if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE)
394 * The fancy G_FILE_ERROR_* codes are identical to the native
399 DBG("Unable to load %s: %s", pathname, error->message);
400 g_clear_error(&error);
402 g_key_file_free(*keyfile);
408 static int load_policy(GKeyFile *keyfile, const char *groupname,
409 struct policy_group *group)
411 struct connman_session_config *config = group->config;
415 group->selinux = g_key_file_get_string(keyfile, groupname,
418 group->gid = g_key_file_get_string(keyfile, groupname,
421 group->uid = g_key_file_get_string(keyfile, groupname,
424 if (group->selinux == NULL && group->gid == NULL && group->uid == NULL)
427 config->priority = g_key_file_get_boolean(keyfile, groupname,
430 str = g_key_file_get_string(keyfile, groupname, "RoamingPolicy",
433 config->roaming_policy = connman_session_parse_roaming_policy(str);
437 str = g_key_file_get_string(keyfile, groupname, "ConnectionType",
440 config->type = connman_session_parse_connection_type(str);
444 config->ecall = g_key_file_get_boolean(keyfile, groupname,
445 "EmergencyCall", NULL);
447 str = g_key_file_get_string(keyfile, groupname, "AllowedBearers",
450 tokens = g_strsplit(str, " ", 0);
452 for (i = 0; tokens[i] != NULL; i++) {
453 err = connman_session_parse_bearers(tokens[i],
454 &config->allowed_bearers);
463 DBG("group %p selinux %s uid %s gid %s", group, group->selinux,
464 group->uid, group->gid);
469 static void update_session(struct policy_config *policy)
471 DBG("policy %p session %p", policy, policy->session);
473 if (policy->session == NULL)
476 if (connman_session_config_update(policy->session) < 0)
477 connman_session_destroy(policy->session);
480 static void set_default_config(gpointer user_data)
482 struct policy_config *policy = user_data;
484 connman_session_set_default_config(policy->config);
485 policy->group = NULL;
486 update_session(policy);
489 static void cleanup_config(gpointer user_data)
491 struct policy_config *policy = user_data;
493 DBG("policy %p group %p", policy, policy->group);
495 if (policy->group != NULL)
496 policy->group->sessions =
497 g_slist_remove(policy->group->sessions, policy);
499 g_slist_free(policy->config->allowed_bearers);
500 g_free(policy->config->id);
501 g_free(policy->config);
502 g_free(policy->selinux_context);
503 g_free(policy->selinux);
505 g_slist_free_full(policy->gids, g_free);
509 static void cleanup_group(gpointer user_data)
511 struct policy_group *group = user_data;
513 DBG("group %p", group);
515 g_slist_free_full(group->sessions, set_default_config);
517 g_slist_free(group->config->allowed_bearers);
518 g_free(group->config->id);
519 g_free(group->config);
520 if (group->selinux != NULL)
521 g_hash_table_remove(selinux_hash, group->selinux);
522 if (group->uid != NULL)
523 g_hash_table_remove(uid_hash, group->uid);
524 if (group->gid != NULL)
525 g_hash_table_remove(gid_hash, group->gid);
526 g_free(group->selinux);
532 static void cleanup_file(gpointer user_data)
534 struct policy_file *file = user_data;
536 DBG("file %p", file);
538 g_slist_free_full(file->groups, cleanup_group);
542 static void recheck_sessions(void)
546 struct policy_group *group = NULL;
549 g_hash_table_iter_init(&iter, session_hash);
550 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
551 struct policy_config *policy = value;
553 if (policy->group != NULL)
556 group = g_hash_table_lookup(selinux_hash, policy->selinux);
558 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_LSM;
559 g_free(policy->config->id);
560 policy->config->id = g_strdup(policy->selinux_context);
561 update_session(policy);
565 group = g_hash_table_lookup(uid_hash, policy->uid);
567 set_policy(policy, group);
569 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_UID;
570 g_free(policy->config->id);
571 policy->config->id = g_strdup(policy->uid);
572 update_session(policy);
576 for (list = policy->gids; list != NULL; list = list->next) {
577 char *gid = list->data;
578 group = g_hash_table_lookup(gid_hash, gid);
580 set_policy(policy, group);
582 policy->config->id_type = CONNMAN_SESSION_ID_TYPE_GID;
583 g_free(policy->config->id);
584 policy->config->id = g_strdup(gid);
585 update_session(policy);
591 static int load_file(const char *filename, struct policy_file *file)
594 struct policy_group *group;
601 pathname = g_strdup_printf("%s/%s", POLICYDIR, filename);
602 err = load_keyfile(pathname, &keyfile);
608 groupnames = g_key_file_get_groups(keyfile, NULL);
610 for (i = 0; groupnames[i] != NULL; i++) {
611 group = g_new0(struct policy_group, 1);
612 group->config = g_new0(struct connman_session_config, 1);
614 err = load_policy(keyfile, groupnames[i], group);
616 g_free(group->config);
620 if (group->selinux != NULL)
621 g_hash_table_replace(selinux_hash, group->selinux, group);
623 if (group->uid != NULL)
624 g_hash_table_replace(uid_hash, group->uid, group);
626 if (group->gid != NULL)
627 g_hash_table_replace(gid_hash, group->gid, group);
629 file->groups = g_slist_prepend(file->groups, group);
632 g_strfreev(groupnames);
635 g_slist_free_full(file->groups, cleanup_group);
637 g_key_file_free(keyfile);
642 static connman_bool_t is_filename_valid(const char *filename)
644 if (filename == NULL)
647 if (filename[0] == '.')
650 return g_str_has_suffix(filename, ".policy");
653 static int read_policies()
656 const gchar *filename;
657 struct policy_file *file;
661 dir = g_dir_open(POLICYDIR, 0, NULL);
665 while ((filename = g_dir_read_name(dir)) != NULL) {
666 if (is_filename_valid(filename) == FALSE)
669 file = g_new0(struct policy_file, 1);
670 if (load_file(filename, file) < 0) {
675 g_hash_table_replace(file_hash, g_strdup(filename), file);
684 static void notify_handler(struct inotify_event *event,
685 const char *filename)
687 struct policy_file *file;
689 DBG("event %x file %s", event->mask, filename);
691 if (event->mask & IN_CREATE)
694 if (is_filename_valid(filename) == FALSE)
698 * load_file() will modify the global selinux/uid/gid hash
699 * tables. We need to remove the old entries first before
700 * else the table points to the wrong entries.
702 g_hash_table_remove(file_hash, filename);
704 if (event->mask & (IN_DELETE | IN_MOVED_FROM))
707 if (event->mask & (IN_MOVED_TO | IN_MODIFY)) {
708 connman_info("Policy update for '%s'", filename);
710 file = g_new0(struct policy_file, 1);
711 if (load_file(filename, file) < 0) {
716 g_hash_table_replace(file_hash, g_strdup(filename), file);
721 static int session_policy_local_init(void)
727 /* If the dir doesn't exist, create it */
728 if (g_file_test(POLICYDIR, G_FILE_TEST_IS_DIR) == FALSE) {
729 if (mkdir(POLICYDIR, MODE) < 0) {
735 connection = connman_dbus_get_connection();
736 if (connection == NULL)
739 file_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
740 g_free, cleanup_file);
741 session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
742 NULL, cleanup_config);
743 selinux_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
745 uid_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
747 gid_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
750 err = connman_inotify_register(POLICYDIR, notify_handler);
754 err = connman_session_policy_register(&session_policy_local);
764 connman_inotify_unregister(POLICYDIR, notify_handler);
767 if (file_hash != NULL)
768 g_hash_table_destroy(file_hash);
770 if (session_hash != NULL)
771 g_hash_table_destroy(session_hash);
773 if (selinux_hash != NULL)
774 g_hash_table_destroy(selinux_hash);
776 if (uid_hash != NULL)
777 g_hash_table_destroy(uid_hash);
779 if (gid_hash != NULL)
780 g_hash_table_destroy(gid_hash);
782 connman_session_policy_unregister(&session_policy_local);
784 dbus_connection_unref(connection);
789 static void session_policy_local_exit(void)
793 g_hash_table_destroy(file_hash);
794 g_hash_table_destroy(session_hash);
795 g_hash_table_destroy(selinux_hash);
796 g_hash_table_destroy(uid_hash);
797 g_hash_table_destroy(gid_hash);
799 connman_session_policy_unregister(&session_policy_local);
801 dbus_connection_unref(connection);
803 connman_inotify_unregister(POLICYDIR, notify_handler);
806 CONNMAN_PLUGIN_DEFINE(session_policy_local,
807 "Session local file policy configuration plugin",
808 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
809 session_policy_local_init, session_policy_local_exit)