From: Daniel Wagner Date: Fri, 31 May 2013 07:13:32 +0000 (+0200) Subject: session_policy_local: Rework policy file handling X-Git-Tag: upstream/1.24~37 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f71ccc8e4cdea55979760771966b938159cce1c6;p=platform%2Fupstream%2Fconnman.git session_policy_local: Rework policy file handling The old assumption was that a config file is associtated with one session only. With introducing UID/GID support a policy might be used for several sessions. Furthermore, it was assumed that the file name is the key/ident to identify a session and a file containts exactly one policy. Here are the new rules for writing a policy file. - A valid file name contains letters or numbers and must have a '.policy' suffix. - The file name has not semantical meaning - A policy file may contain contain more than 1 policy - Each policy entry starts with 'policy_' - Each policy entry shall have one and exactly one valid key (e.g. selinux) The implementation has two main hash tables, file_hash and session_hash which owns 'the file' respectively the session config. Additionally there is a hash table which connects a policy with a session (selinux_hash). --- diff --git a/plugins/session_policy_local.c b/plugins/session_policy_local.c index 6cd876c..4b17a4c 100644 --- a/plugins/session_policy_local.c +++ b/plugins/session_policy_local.c @@ -46,33 +46,78 @@ static DBusConnection *connection; -static GHashTable *policy_hash; -static GHashTable *session_hash; +static GHashTable *file_hash; /* (filename, policy_file) */ +static GHashTable *session_hash; /* (connman_session, policy_config) */ + +/* Global lookup table for mapping sessions to policies */ +static GHashTable *selinux_hash; /* (lsm context, policy_group) */ struct create_data { struct connman_session *session; }; -struct policy_data { - int refcount; - char *ident; +/* + * A instance of struct policy_file is created per file in + * POLICYDIR. + */ +struct policy_file { + /* + * A valid file is a keyfile with one ore more groups. All + * groups are keept in this list. + */ + GSList *groups; +}; - struct connman_session *session; +struct policy_group { + char *selinux; + + /* + * Each policy_group owns a config and is not shared with + * sessions. Instead each session copies the valued from this + * object. + */ struct connman_session_config *config; + + /* All 'users' of this policy. */ + GSList *sessions; +}; + +/* A struct policy_config object is created and owned by a session. */ +struct policy_config { + char *selinux; + + /* The policy config owned by the session */ + struct connman_session_config *config; + + /* To which policy belongs this policy_config */ + struct connman_session *session; + /* + * Points to the policy_group when a config has been applied + * from a file. + */ + struct policy_group *group; }; -static void cleanup_policy(gpointer user_data) +static void copy_session_config(struct connman_session_config *dst, + struct connman_session_config *src) { - struct policy_data *policy = user_data; + g_slist_free(dst->allowed_bearers); + dst->allowed_bearers = g_slist_copy(src->allowed_bearers); + dst->ecall = src->ecall; + dst->type = src->type; + dst->roaming_policy = src->roaming_policy; + dst->priority = src->priority; +} - DBG("policy %p", policy); +static void set_policy(struct policy_config *policy, + struct policy_group *group) +{ + DBG("policy %p group %p", policy, group); - if (policy->config != NULL) - g_slist_free(policy->config->allowed_bearers); + group->sessions = g_slist_prepend(group->sessions, policy); + policy->group = group; - g_free(policy->ident); - g_free(policy->config); - g_free(policy); + copy_session_config(policy->config, group->config); } static char *parse_selinux_type(const char *context) @@ -111,51 +156,27 @@ static char *parse_selinux_type(const char *context) return ident; } -static struct policy_data *create_policy(const char *ident) +static struct policy_config *create_policy(void) { - struct policy_data *policy; + struct policy_config *policy; - DBG("ident %s", ident); - - policy = g_new0(struct policy_data, 1); - policy->refcount = 1; + policy = g_new0(struct policy_config, 1); DBG("policy %p", policy); policy->config = connman_session_create_default_config(); - policy->ident = g_strdup(ident); - - g_hash_table_replace(policy_hash, policy->ident, policy); return policy; } -static struct policy_data *policy_ref(struct policy_data *policy) -{ - DBG("%p %s ref %d", policy, policy->ident, policy->refcount + 1); - - __sync_fetch_and_add(&policy->refcount, 1); - - return policy; -} - -static void policy_unref(struct policy_data *policy) -{ - DBG(" %p %s ref %d", policy, policy->ident, policy->refcount - 1); - - if (__sync_fetch_and_sub(&policy->refcount, 1) != 1) - return; - - g_hash_table_remove(policy_hash, policy->ident); -}; - static void selinux_context_reply(const unsigned char *context, void *user_data, int err) { struct cb_data *cbd = user_data; connman_session_config_func_t cb = cbd->cb; struct create_data *data = cbd->data; - struct policy_data *policy; + struct policy_config *policy; + struct policy_group *group; struct connman_session_config *config = NULL; char *ident = NULL; @@ -172,12 +193,13 @@ static void selinux_context_reply(const unsigned char *context, void *user_data, goto done; } - policy = g_hash_table_lookup(policy_hash, ident); - if (policy != NULL) { - policy_ref(policy); - policy->session = data->session; - } else - policy = create_policy(ident); + policy = create_policy(); + policy->selinux = g_strdup(ident); + policy->session = data->session; + + group = g_hash_table_lookup(selinux_hash, policy->selinux); + if (group != NULL) + set_policy(policy, group); g_hash_table_replace(session_hash, data->session, policy); config = policy->config; @@ -232,8 +254,6 @@ static void policy_local_destroy(struct connman_session *session) return; g_hash_table_remove(session_hash, session); - policy->session = NULL; - policy_unref(policy); } static struct connman_session_policy session_policy_local = { @@ -248,8 +268,6 @@ static int load_keyfile(const char *pathname, GKeyFile **keyfile) GError *error = NULL; int err; - DBG("Loading %s", pathname); - *keyfile = g_key_file_new(); if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE) @@ -273,57 +291,41 @@ err: return err; } -static int load_policy(struct policy_data *policy) +static int load_policy(GKeyFile *keyfile, const char *groupname, + struct policy_group *group) { - struct connman_session_config *config = policy->config; - GKeyFile *keyfile; - char *pathname; + struct connman_session_config *config = group->config; char *str, **tokens; int i, err = 0; - connman_session_set_default_config(config); - - pathname = g_strdup_printf("%s/%s", POLICYDIR, policy->ident); + group->selinux = g_key_file_get_string(keyfile, groupname, + "selinux", NULL); + if (group->selinux == NULL) + return -EINVAL; - err = load_keyfile(pathname, &keyfile); - if (err < 0) { - g_free(pathname); - - if (err == -ENOENT) { - /* Ignore empty files */ - return 0; - } - - return err; - } - - config->priority = g_key_file_get_boolean(keyfile, "Default", + config->priority = g_key_file_get_boolean(keyfile, groupname, "Priority", NULL); - str = g_key_file_get_string(keyfile, "Default", "RoamingPolicy", + str = g_key_file_get_string(keyfile, groupname, "RoamingPolicy", NULL); if (str != NULL) { config->roaming_policy = connman_session_parse_roaming_policy(str); g_free(str); } - str = g_key_file_get_string(keyfile, "Default", "ConnectionType", + str = g_key_file_get_string(keyfile, groupname, "ConnectionType", NULL); if (str != NULL) { config->type = connman_session_parse_connection_type(str); g_free(str); } - config->ecall = g_key_file_get_boolean(keyfile, "Default", + config->ecall = g_key_file_get_boolean(keyfile, groupname, "EmergencyCall", NULL); - str = g_key_file_get_string(keyfile, "Default", "AllowedBearers", + str = g_key_file_get_string(keyfile, groupname, "AllowedBearers", NULL); - if (str != NULL) { - g_slist_free(config->allowed_bearers); - config->allowed_bearers = NULL; - tokens = g_strsplit(str, " ", 0); for (i = 0; tokens[i] != NULL; i++) { @@ -337,13 +339,11 @@ static int load_policy(struct policy_data *policy) g_strfreev(tokens); } - g_key_file_free(keyfile); - g_free(pathname); + DBG("group %p selinux %s", group, group->selinux); return err; } - -static void update_session(struct policy_data *policy) +static void update_session(struct policy_config *policy) { DBG("policy %p session %p", policy, policy->session); @@ -354,90 +354,199 @@ static void update_session(struct policy_data *policy) connman_session_destroy(policy->session); } -static void remove_policy(struct policy_data *policy) +static void set_default_config(gpointer user_data) { - if (policy->session != NULL) - connman_session_set_default_config(policy->config); + struct policy_config *policy = user_data; + + connman_session_set_default_config(policy->config); + policy->group = NULL; update_session(policy); +} + +static void cleanup_config(gpointer user_data) +{ + struct policy_config *policy = user_data; + + DBG("policy %p group %p", policy, policy->group); - policy_unref(policy); + if (policy->group != NULL) + policy->group->sessions = + g_slist_remove(policy->group->sessions, policy); + + g_slist_free(policy->config->allowed_bearers); + g_free(policy->config); + g_free(policy->selinux); + g_free(policy); } -static void notify_handler(struct inotify_event *event, - const char *ident) +static void cleanup_group(gpointer user_data) { - struct policy_data *policy; - int err; + struct policy_group *group = user_data; - if (ident == NULL) - return; + DBG("group %p", group); - policy = g_hash_table_lookup(policy_hash, ident); + g_slist_free_full(group->sessions, set_default_config); - if (event->mask & (IN_CREATE | IN_MOVED_TO)) { - connman_info("Policy added for '%s'", ident); + g_slist_free(group->config->allowed_bearers); + g_free(group->config); + if (group->selinux != NULL) + g_hash_table_remove(selinux_hash, group->selinux); + g_free(group->selinux); + g_free(group); +} - if (policy != NULL) - policy_ref(policy); - else - policy = create_policy(ident); +static void cleanup_file(gpointer user_data) +{ + struct policy_file *file = user_data; - err = load_policy(policy); - if (err < 0) { - connman_warn("Loading policy file '%s' failed with %s", - ident, strerror(-err)); - return; + DBG("file %p", file); + + g_slist_free_full(file->groups, cleanup_group); + g_free(file); +} + +static void recheck_sessions(void) +{ + GHashTableIter iter; + gpointer value, key; + struct policy_group *group = NULL; + + g_hash_table_iter_init(&iter, session_hash); + while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + struct policy_config *policy = value; + + if (policy->group != NULL) + continue; + + group = g_hash_table_lookup(selinux_hash, policy->selinux); + if (group != NULL) { + set_policy(policy, group); + update_session(policy); } } +} - if (policy == NULL) - return; +static int load_file(const char *filename, struct policy_file *file) +{ + GKeyFile *keyfile; + struct policy_group *group; + char **groupnames; + char *pathname; + int err = 0, i; - if (event->mask & IN_MODIFY) { - connman_info("Policy modifed for '%s'", ident); + DBG("%s", filename); - err = load_policy(policy); + pathname = g_strdup_printf("%s/%s", POLICYDIR, filename); + err = load_keyfile(pathname, &keyfile); + g_free(pathname); + + if (err < 0) + return err; + + groupnames = g_key_file_get_groups(keyfile, NULL); + + for (i = 0; groupnames[i] != NULL; i++) { + group = g_new0(struct policy_group, 1); + group->config = g_new0(struct connman_session_config, 1); + + err = load_policy(keyfile, groupnames[i], group); if (err < 0) { - connman_warn("Loading policy file '%s' failed with %s", - ident, strerror(-err)); - return; + g_free(group->config); + g_free(group); + break; } + g_hash_table_replace(selinux_hash, group->selinux, group); + + file->groups = g_slist_prepend(file->groups, group); } - if (event->mask & (IN_DELETE | IN_MOVED_FROM)) { - connman_info("Policy deleted for '%s'", ident); + g_strfreev(groupnames); - remove_policy(policy); - return; - } + if (err < 0) + g_slist_free_full(file->groups, cleanup_group); - update_session(policy); + g_key_file_free(keyfile); + + return err; +} + +static connman_bool_t is_filename_valid(const char *filename) +{ + if (filename == NULL) + return FALSE; + + if (filename[0] == '.') + return FALSE; + + return g_str_has_suffix(filename, ".policy"); } -static int read_policies(void) +static int read_policies() { GDir *dir; - int err = 0; + const gchar *filename; + struct policy_file *file; DBG(""); dir = g_dir_open(POLICYDIR, 0, NULL); - if (dir != NULL) { - const gchar *file; + if (dir == NULL) + return -EINVAL; - while ((file = g_dir_read_name(dir)) != NULL) { - struct policy_data *policy; + while ((filename = g_dir_read_name(dir)) != NULL) { + if (is_filename_valid(filename) == FALSE) + continue; - policy = create_policy(file); - err = load_policy(policy); - if (err < 0) - break; + file = g_new0(struct policy_file, 1); + if (load_file(filename, file) < 0) { + g_free(file); + continue; } - g_dir_close(dir); + g_hash_table_replace(file_hash, g_strdup(filename), file); } - return err; + g_dir_close(dir); + + return 0; +} + + +static void notify_handler(struct inotify_event *event, + const char *filename) +{ + struct policy_file *file; + + DBG("event %x file %s", event->mask, filename); + + if (event->mask & IN_CREATE) + return; + + if (is_filename_valid(filename) == FALSE) + return; + + /* + * load_file() will modify the global selinux/uid/gid hash + * tables. We need to remove the old entries first before + * else the table points to the wrong entries. + */ + g_hash_table_remove(file_hash, filename); + + if (event->mask & (IN_DELETE | IN_MOVED_FROM)) + return; + + if (event->mask & (IN_MOVED_TO | IN_MODIFY)) { + connman_info("Policy update for '%s'", filename); + + file = g_new0(struct policy_file, 1); + if (load_file(filename, file) < 0) { + g_free(file); + return; + } + + g_hash_table_replace(file_hash, g_strdup(filename), file); + recheck_sessions(); + } } static int session_policy_local_init(void) @@ -458,23 +567,23 @@ static int session_policy_local_init(void) if (connection == NULL) return -EIO; + file_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, cleanup_file); session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, NULL); - policy_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, cleanup_policy); + NULL, cleanup_config); + selinux_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, NULL); err = connman_inotify_register(POLICYDIR, notify_handler); if (err < 0) goto err; - err = read_policies(); - if (err < 0) - goto err_notify; - err = connman_session_policy_register(&session_policy_local); if (err < 0) goto err_notify; + read_policies(); + return 0; err_notify: @@ -482,10 +591,14 @@ err_notify: connman_inotify_unregister(POLICYDIR, notify_handler); err: + if (file_hash != NULL) + g_hash_table_destroy(file_hash); + if (session_hash != NULL) g_hash_table_destroy(session_hash); - if (policy_hash != NULL) - g_hash_table_destroy(policy_hash); + + if (selinux_hash != NULL) + g_hash_table_destroy(selinux_hash); connman_session_policy_unregister(&session_policy_local); @@ -498,8 +611,9 @@ static void session_policy_local_exit(void) { DBG(""); + g_hash_table_destroy(file_hash); g_hash_table_destroy(session_hash); - g_hash_table_destroy(policy_hash); + g_hash_table_destroy(selinux_hash); connman_session_policy_unregister(&session_policy_local);