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