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 *policy_hash;
50 static GHashTable *session_hash;
53 struct connman_session *session;
60 struct connman_session *session;
61 struct connman_session_config *config;
64 static void cleanup_policy(gpointer user_data)
66 struct policy_data *policy = user_data;
68 DBG("policy %p", policy);
70 if (policy->config != NULL)
71 g_slist_free(policy->config->allowed_bearers);
73 g_free(policy->ident);
74 g_free(policy->config);
78 static char *parse_selinux_type(const char *context)
80 char *ident, **tokens;
83 * SELinux combines Role-Based Access Control (RBAC), Type
84 * Enforcment (TE) and optionally Multi-Level Security (MLS).
86 * When SELinux is enabled all processes and files are labeled
87 * with a contex that contains information such as user, role
88 * type (and optionally a level). E.g.
91 * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
93 * For identifyng application we (ab)using the type
94 * information. In the above example the haifux_exec_t type
95 * will be transfered to haifux_t as defined in the domain
96 * transition and thus we are able to identify the application
100 tokens = g_strsplit(context, ":", 0);
101 if (g_strv_length(tokens) < 2) {
106 /* Use the SELinux type as identification token. */
107 ident = g_strdup(tokens[2]);
114 static struct policy_data *create_policy(const char *ident)
116 struct policy_data *policy;
118 DBG("ident %s", ident);
120 policy = g_new0(struct policy_data, 1);
121 policy->refcount = 1;
123 DBG("policy %p", policy);
125 policy->config = connman_session_create_default_config();
126 policy->ident = g_strdup(ident);
128 g_hash_table_replace(policy_hash, policy->ident, policy);
133 static struct policy_data *policy_ref(struct policy_data *policy)
135 DBG("%p %s ref %d", policy, policy->ident, policy->refcount + 1);
137 __sync_fetch_and_add(&policy->refcount, 1);
142 static void policy_unref(struct policy_data *policy)
144 DBG(" %p %s ref %d", policy, policy->ident, policy->refcount - 1);
146 if (__sync_fetch_and_sub(&policy->refcount, 1) != 1)
149 g_hash_table_remove(policy_hash, policy->ident);
152 static void selinux_context_reply(const unsigned char *context, void *user_data,
155 struct cb_data *cbd = user_data;
156 connman_session_config_func_t cb = cbd->cb;
157 struct create_data *data = cbd->data;
158 struct policy_data *policy;
159 struct connman_session_config *config = NULL;
162 DBG("session %p", data->session);
167 DBG("SELinux context %s", context);
169 ident = parse_selinux_type((const char*)context);
175 policy = g_hash_table_lookup(policy_hash, ident);
176 if (policy != NULL) {
178 policy->session = data->session;
180 policy = create_policy(ident);
182 g_hash_table_replace(session_hash, data->session, policy);
183 config = policy->config;
186 (*cb)(data->session, config, cbd->user_data, err);
193 static int policy_local_create(struct connman_session *session,
194 connman_session_config_func_t cb,
197 struct cb_data *cbd = cb_data_new(cb, user_data);
198 struct create_data *data;
202 DBG("session %p", session);
204 data = g_new0(struct create_data, 1);
207 data->session = session;
209 owner = connman_session_get_owner(session);
211 err = connman_dbus_get_selinux_context(connection, owner,
212 selinux_context_reply,
215 connman_error("Could not get SELinux context");
224 static void policy_local_destroy(struct connman_session *session)
226 struct policy_data *policy;
228 DBG("session %p", session);
230 policy = g_hash_table_lookup(session_hash, session);
234 g_hash_table_remove(session_hash, session);
235 policy->session = NULL;
236 policy_unref(policy);
239 static struct connman_session_policy session_policy_local = {
240 .name = "session local policy configuration",
241 .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
242 .create = policy_local_create,
243 .destroy = policy_local_destroy,
246 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
248 GError *error = NULL;
251 DBG("Loading %s", pathname);
253 *keyfile = g_key_file_new();
255 if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE)
262 * The fancy G_FILE_ERROR_* codes are identical to the native
267 DBG("Unable to load %s: %s", pathname, error->message);
268 g_clear_error(&error);
270 g_key_file_free(*keyfile);
276 static int load_policy(struct policy_data *policy)
278 struct connman_session_config *config = policy->config;
284 connman_session_set_default_config(config);
286 pathname = g_strdup_printf("%s/%s", POLICYDIR, policy->ident);
288 err = load_keyfile(pathname, &keyfile);
292 if (err == -ENOENT) {
293 /* Ignore empty files */
300 config->priority = g_key_file_get_boolean(keyfile, "Default",
303 str = g_key_file_get_string(keyfile, "Default", "RoamingPolicy",
306 config->roaming_policy = connman_session_parse_roaming_policy(str);
310 str = g_key_file_get_string(keyfile, "Default", "ConnectionType",
313 config->type = connman_session_parse_connection_type(str);
317 config->ecall = g_key_file_get_boolean(keyfile, "Default",
318 "EmergencyCall", NULL);
320 str = g_key_file_get_string(keyfile, "Default", "AllowedBearers",
324 g_slist_free(config->allowed_bearers);
325 config->allowed_bearers = NULL;
327 tokens = g_strsplit(str, " ", 0);
329 for (i = 0; tokens[i] != NULL; i++) {
330 err = connman_session_parse_bearers(tokens[i],
331 &config->allowed_bearers);
340 g_key_file_free(keyfile);
346 static void update_session(struct policy_data *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 remove_policy(struct policy_data *policy)
359 if (policy->session != NULL)
360 connman_session_set_default_config(policy->config);
361 update_session(policy);
363 policy_unref(policy);
366 static void notify_handler(struct inotify_event *event,
369 struct policy_data *policy;
375 policy = g_hash_table_lookup(policy_hash, ident);
377 if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
378 connman_info("Policy added for '%s'", ident);
383 policy = create_policy(ident);
385 err = load_policy(policy);
387 connman_warn("Loading policy file '%s' failed with %s",
388 ident, strerror(-err));
396 if (event->mask & IN_MODIFY) {
397 connman_info("Policy modifed for '%s'", ident);
399 err = load_policy(policy);
401 connman_warn("Loading policy file '%s' failed with %s",
402 ident, strerror(-err));
407 if (event->mask & (IN_DELETE | IN_MOVED_FROM)) {
408 connman_info("Policy deleted for '%s'", ident);
410 remove_policy(policy);
414 update_session(policy);
417 static int read_policies(void)
424 dir = g_dir_open(POLICYDIR, 0, NULL);
428 while ((file = g_dir_read_name(dir)) != NULL) {
429 struct policy_data *policy;
431 policy = create_policy(file);
432 err = load_policy(policy);
443 static int session_policy_local_init(void)
449 /* If the dir doesn't exist, create it */
450 if (g_file_test(POLICYDIR, G_FILE_TEST_IS_DIR) == FALSE) {
451 if (mkdir(POLICYDIR, MODE) < 0) {
457 connection = connman_dbus_get_connection();
458 if (connection == NULL)
461 session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
463 policy_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
464 NULL, cleanup_policy);
466 err = connman_inotify_register(POLICYDIR, notify_handler);
470 err = read_policies();
474 err = connman_session_policy_register(&session_policy_local);
482 connman_inotify_unregister(POLICYDIR, notify_handler);
485 if (session_hash != NULL)
486 g_hash_table_destroy(session_hash);
487 if (policy_hash != NULL)
488 g_hash_table_destroy(policy_hash);
490 connman_session_policy_unregister(&session_policy_local);
492 dbus_connection_unref(connection);
497 static void session_policy_local_exit(void)
501 g_hash_table_destroy(session_hash);
502 g_hash_table_destroy(policy_hash);
504 connman_session_policy_unregister(&session_policy_local);
506 dbus_connection_unref(connection);
508 connman_inotify_unregister(POLICYDIR, notify_handler);
511 CONNMAN_PLUGIN_DEFINE(session_policy_local,
512 "Session local file policy configuration plugin",
513 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
514 session_policy_local_init, session_policy_local_exit)