session_policy_local: Rename session_policy_ivi
[platform/upstream/connman.git] / plugins / session_policy_local.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  BMW Car IT GbmH. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <string.h>
28 #include <sys/inotify.h>
29
30 #include <glib.h>
31
32 #include <gdbus.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/log.h>
37 #include <connman/session.h>
38 #include <connman/dbus.h>
39 #include <connman/inotify.h>
40
41 #define POLICYDIR STORAGEDIR "/session_policy_local"
42
43 static DBusConnection *connection;
44
45 static GHashTable *policy_hash;
46 static GHashTable *session_hash;
47
48 struct create_data {
49         struct connman_session *session;
50         connman_session_config_cb callback;
51         void *user_data;
52 };
53
54 struct policy_data {
55         int refcount;
56         char *ident;
57
58         struct connman_session *session;
59         struct connman_session_config *config;
60 };
61
62 static void cleanup_policy(gpointer user_data)
63 {
64         struct policy_data *policy = user_data;
65
66         if (policy->config != NULL)
67                 g_slist_free(policy->config->allowed_bearers);
68
69         g_free(policy->ident);
70         g_free(policy->config);
71         g_free(policy);
72 }
73
74 static char *parse_ident(const unsigned char *context)
75 {
76         char *str, *ident, **tokens;
77
78         /*
79          * SELinux combines Role-Based Access Control (RBAC), Type
80          * Enforcment (TE) and optionally Multi-Level Security (MLS).
81          *
82          * When SELinux is enabled all processes and files are labeled
83          * with a contex that contains information such as user, role
84          * type (and optionally a level). E.g.
85          *
86          * $ ls -Z
87          * -rwxrwxr-x. wagi wagi unconfined_u:object_r:haifux_exec_t:s0 session_ui.py
88          *
89          * For identifyng application we (ab)using the type
90          * information. In the above example the haifux_exec_t type
91          * will be transfered to haifux_t as defined in the domain
92          * transition and thus we are able to identify the application
93          * as haifux_t.
94          */
95
96         str = g_strdup((const gchar*)context);
97         if (str == NULL)
98                 return NULL;
99
100         DBG("SELinux context %s", str);
101
102         tokens = g_strsplit(str, ":", 0);
103         if (tokens == NULL) {
104                 g_free(str);
105                 return NULL;
106         }
107
108         /* Use the SELinux type as identification token. */
109         ident = g_strdup(tokens[2]);
110
111         g_strfreev(tokens);
112         g_free(str);
113
114         return ident;
115 }
116
117 static struct policy_data *create_policy(const char *ident)
118 {
119         struct policy_data *policy;
120
121         DBG("ident %s", ident);
122
123         policy = g_try_new0(struct policy_data, 1);
124         if (policy == NULL)
125                 return NULL;
126
127         policy->config = connman_session_create_default_config();
128         if (policy->config == NULL) {
129                 g_free(policy);
130                 return NULL;
131         }
132
133         policy->refcount = 1;
134         policy->ident = g_strdup(ident);
135
136         g_hash_table_replace(policy_hash, policy->ident, policy);
137
138         return policy;
139 }
140
141 static struct policy_data *policy_ref(struct policy_data *policy)
142 {
143         DBG("%p %s ref %d", policy, policy->ident, policy->refcount + 1);
144
145         __sync_fetch_and_add(&policy->refcount, 1);
146
147         return policy;
148 }
149
150 static void policy_unref(struct policy_data *policy)
151 {
152         DBG(" %p %s ref %d", policy, policy->ident, policy->refcount - 1);
153
154         if (__sync_fetch_and_sub(&policy->refcount, 1) != 1)
155                 return;
156
157         g_hash_table_remove(policy_hash, policy->ident);
158 };
159
160 static void selinux_context_reply(const unsigned char *context, void *user_data,
161                                         int err)
162 {
163         struct create_data *data = user_data;
164         struct policy_data *policy;
165         struct connman_session_config *config = NULL;
166         char *ident = NULL;
167
168         DBG("session %p", data->session);
169
170         if (err < 0)
171                 goto done;
172
173         ident = parse_ident(context);
174         if (ident == NULL) {
175                 err = -EINVAL;
176                 goto done;
177         }
178
179         policy = g_hash_table_lookup(policy_hash, ident);
180         if (policy != NULL) {
181                 policy_ref(policy);
182                 policy->session = data->session;
183         } else {
184                 policy = create_policy(ident);
185                 if (policy == NULL) {
186                         err = -ENOMEM;
187                         goto done;
188                 }
189         }
190
191         g_hash_table_replace(session_hash, data->session, policy);
192         config = policy->config;
193
194 done:
195         (*data->callback)(data->session, config, data->user_data, err);
196
197         g_free(data);
198         g_free(ident);
199 }
200
201 static int policy_local_create(struct connman_session *session,
202                                 connman_session_config_cb callback,
203                                 void *user_data)
204 {
205         struct create_data *data;
206         const char *owner;
207         int err;
208
209         DBG("session %p", session);
210
211         data = g_try_new0(struct create_data, 1);
212         if (data == NULL)
213                 return -ENOMEM;
214
215         data->session = session;
216         data->callback = callback;
217         data->user_data = user_data;
218
219         owner = connman_session_get_owner(session);
220
221         err = connman_dbus_get_selinux_context(connection, owner,
222                                         selinux_context_reply,
223                                         data);
224         if (err < 0) {
225                 connman_error("Could not get SELinux context");
226                 g_free(data);
227                 return err;
228         }
229
230         return 0;
231 }
232
233 static void policy_local_destroy(struct connman_session *session)
234 {
235         struct policy_data *policy;
236
237         DBG("session %p", session);
238
239         policy = g_hash_table_lookup(session_hash, session);
240         g_hash_table_remove(session_hash, session);
241         policy->session = NULL;
242
243         policy_unref(policy);
244 }
245
246 static struct connman_session_policy session_policy_local = {
247         .name = "session local policy configuration",
248         .priority = CONNMAN_SESSION_POLICY_PRIORITY_DEFAULT,
249         .create = policy_local_create,
250         .destroy = policy_local_destroy,
251 };
252
253 static int load_keyfile(const char *pathname, GKeyFile **keyfile)
254 {
255         GError *error = NULL;
256         int err;
257
258         DBG("Loading %s", pathname);
259
260         *keyfile = g_key_file_new();
261
262         if (g_key_file_load_from_file(*keyfile, pathname, 0, &error) == FALSE)
263                 goto err;
264
265         return 0;
266
267 err:
268         /*
269          * The fancy G_FILE_ERROR_* codes are identical to the native
270          * error codes.
271          */
272         err = -error->code;
273
274         DBG("Unable to load %s: %s", pathname, error->message);
275         g_clear_error(&error);
276
277         g_key_file_free(*keyfile);
278         *keyfile = NULL;
279
280         return err;
281 }
282
283 static int load_policy(struct policy_data *policy)
284 {
285         struct connman_session_config *config = policy->config;
286         GKeyFile *keyfile;
287         char *pathname;
288         char *str, **tokens;
289         int i, err = 0;
290
291         pathname = g_strdup_printf("%s/%s", POLICYDIR, policy->ident);
292         if(pathname == NULL)
293                 return -ENOMEM;
294
295         err = load_keyfile(pathname, &keyfile);
296         if (err < 0) {
297                 g_free(pathname);
298
299                 if (err == -ENOENT) {
300                         /* Ignore empty files */
301                         return 0;
302                 }
303
304                 return err;
305         }
306
307         config->priority = g_key_file_get_boolean(keyfile, "Default",
308                                                 "Priority", NULL);
309
310         str = g_key_file_get_string(keyfile, "Default", "RoamingPolicy",
311                                 NULL);
312         if (str != NULL) {
313                 config->roaming_policy = connman_session_parse_roaming_policy(str);
314                 g_free(str);
315         } else {
316                 config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
317         }
318
319         str = g_key_file_get_string(keyfile, "Default", "ConnectionType",
320                                 NULL);
321         if (str != NULL) {
322                 config->type = connman_session_parse_connection_type(str);
323                 g_free(str);
324         } else {
325                 config->type = CONNMAN_SESSION_TYPE_ANY;
326         }
327
328         config->ecall = g_key_file_get_boolean(keyfile, "Default",
329                                                 "EmergencyCall", NULL);
330
331         g_slist_free(config->allowed_bearers);
332         config->allowed_bearers = NULL;
333
334         str = g_key_file_get_string(keyfile, "Default", "AllowedBearers",
335                                 NULL);
336
337         if (str != NULL) {
338                 tokens = g_strsplit(str, " ", 0);
339
340                 for (i = 0; tokens[i] != NULL; i++) {
341                         err = connman_session_parse_bearers(tokens[i],
342                                         &config->allowed_bearers);
343                         if (err < 0)
344                                 break;
345                 }
346
347                 g_free(str);
348                 g_strfreev(tokens);
349         } else {
350                 config->allowed_bearers = g_slist_append(NULL,
351                                 GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
352                 if (config->allowed_bearers == NULL)
353                         err = -ENOMEM;
354         }
355
356         g_key_file_free(keyfile);
357         g_free(pathname);
358
359         return err;
360 }
361
362 static void update_session(struct connman_session *session)
363 {
364         if (connman_session_config_update(session) < 0)
365                 connman_session_destroy(session);
366 }
367
368 static void remove_policy(struct policy_data *policy)
369 {
370         connman_bool_t update = FALSE;
371         int err;
372
373         if (policy->session != NULL)
374                 update = TRUE;
375
376         policy_unref(policy);
377
378         if (update == FALSE)
379                 return;
380
381         err = connman_session_set_default_config(policy->config);
382         if (err < 0) {
383                 connman_session_destroy(policy->session);
384                 return;
385         }
386
387         update_session(policy->session);
388 }
389
390 static void notify_handler(struct inotify_event *event,
391                                         const char *ident)
392 {
393         struct policy_data *policy;
394
395         if (ident == NULL)
396                 return;
397
398         policy = g_hash_table_lookup(policy_hash, ident);
399
400         if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
401                 connman_info("Policy added for '%s'", ident);
402
403                 if (policy != NULL)
404                         policy_ref(policy);
405                 else
406                         policy = create_policy(ident);
407         }
408
409         if (policy == NULL)
410                 return;
411
412         if (event->mask & IN_MODIFY) {
413                 connman_info("Policy modifed for '%s'", ident);
414
415                 if (load_policy(policy) < 0) {
416                         remove_policy(policy);
417                         return;
418                 }
419         }
420
421         if (event->mask & (IN_DELETE | IN_MOVED_FROM)) {
422                 connman_info("Policy deleted for '%s'", ident);
423
424                 remove_policy(policy);
425                 return;
426         }
427
428         if (policy->session != NULL)
429                 update_session(policy->session);
430 }
431
432 static int read_policies(void)
433 {
434         GDir *dir;
435         int err = 0;
436
437         DBG("");
438
439         dir = g_dir_open(POLICYDIR, 0, NULL);
440         if (dir != NULL) {
441                 const gchar *file;
442
443                 while ((file = g_dir_read_name(dir)) != NULL) {
444                         struct policy_data *policy;
445
446                         policy = create_policy(file);
447                         if (policy == NULL) {
448                                 err = -ENOMEM;
449                                 break;
450                         }
451
452                         err = load_policy(policy);
453                         if (err < 0)
454                                 break;
455                 }
456
457                 g_dir_close(dir);
458         }
459
460         return err;
461 }
462
463 static int session_policy_local_init(void)
464 {
465         int err;
466
467         connection = connman_dbus_get_connection();
468         if (connection == NULL)
469                 return -EIO;
470
471         session_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
472                                                 NULL, NULL);
473         if (session_hash == NULL) {
474                 err = -ENOMEM;
475                 goto err;
476         }
477
478         policy_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
479                                         NULL, cleanup_policy);
480         if (policy_hash == NULL) {
481                 err = -ENOMEM;
482                 goto err;
483         }
484
485         err = connman_inotify_register(POLICYDIR, notify_handler);
486         if (err < 0)
487                 goto err;
488
489         err = read_policies();
490         if (err < 0)
491                 goto err_notify;
492
493         err = connman_session_policy_register(&session_policy_local);
494         if (err < 0)
495                 goto err_notify;
496
497         return 0;
498
499 err_notify:
500
501         connman_inotify_unregister(POLICYDIR, notify_handler);
502
503 err:
504         if (session_hash != NULL)
505                 g_hash_table_destroy(session_hash);
506         if (policy_hash != NULL)
507                 g_hash_table_destroy(policy_hash);
508
509         connman_session_policy_unregister(&session_policy_local);
510
511         dbus_connection_unref(connection);
512
513         return err;
514 }
515
516 static void session_policy_local_exit(void)
517 {
518         g_hash_table_destroy(session_hash);
519         g_hash_table_destroy(policy_hash);
520
521         connman_session_policy_unregister(&session_policy_local);
522
523         dbus_connection_unref(connection);
524
525         connman_inotify_unregister(POLICYDIR, notify_handler);
526 }
527
528 CONNMAN_PLUGIN_DEFINE(session_policy_local,
529                 "Session local file policy configuration plugin",
530                 VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
531                 session_policy_local_init, session_policy_local_exit)