config: Fix service pointers memory leak
[platform/upstream/connman.git] / src / config.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. 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 <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/vfs.h>
30 #include <sys/inotify.h>
31 #include <glib.h>
32
33 #include "connman.h"
34
35 struct connman_config_service {
36         char *ident;
37         char *name;
38         char *type;
39         void *ssid;
40         unsigned int ssid_len;
41         char *eap;
42         char *identity;
43         char *ca_cert_file;
44         char *client_cert_file;
45         char *private_key_file;
46         char *private_key_passphrase;
47         char *private_key_passphrase_type;
48         char *phase2;
49         char *passphrase;
50         connman_bool_t from_fs;
51 };
52
53 struct connman_config {
54         char *ident;
55         char *name;
56         char *description;
57         connman_bool_t protected;
58         GHashTable *service_table;
59 };
60
61 static GHashTable *config_table = NULL;
62 static GSList *protected_services = NULL;
63
64 static int inotify_wd = -1;
65
66 static GIOChannel *inotify_channel = NULL;
67 static uint inotify_watch = 0;
68
69 #define NONFS_CONFIG_NAME                "internal"
70
71 /* Definition of possible strings in the .config files */
72 #define CONFIG_KEY_NAME                "Name"
73 #define CONFIG_KEY_DESC                "Description"
74 #define CONFIG_KEY_PROT                "Protected"
75
76 #define SERVICE_KEY_TYPE               "Type"
77 #define SERVICE_KEY_NAME               "Name"
78 #define SERVICE_KEY_SSID               "SSID"
79 #define SERVICE_KEY_EAP                "EAP"
80 #define SERVICE_KEY_CA_CERT            "CACertFile"
81 #define SERVICE_KEY_CL_CERT            "ClientCertFile"
82 #define SERVICE_KEY_PRV_KEY            "PrivateKeyFile"
83 #define SERVICE_KEY_PRV_KEY_PASS       "PrivateKeyPassphrase"
84 #define SERVICE_KEY_PRV_KEY_PASS_TYPE  "PrivateKeyPassphraseType"
85 #define SERVICE_KEY_IDENTITY           "Identity"
86 #define SERVICE_KEY_PHASE2             "Phase2"
87 #define SERVICE_KEY_PASSPHRASE         "Passphrase"
88
89 static const char *config_possible_keys[] = {
90         CONFIG_KEY_NAME,
91         CONFIG_KEY_DESC,
92         CONFIG_KEY_PROT,
93         NULL,
94 };
95
96 static const char *service_possible_keys[] = {
97         SERVICE_KEY_TYPE,
98         SERVICE_KEY_NAME,
99         SERVICE_KEY_SSID,
100         SERVICE_KEY_EAP,
101         SERVICE_KEY_CA_CERT,
102         SERVICE_KEY_CL_CERT,
103         SERVICE_KEY_PRV_KEY,
104         SERVICE_KEY_PRV_KEY_PASS,
105         SERVICE_KEY_PRV_KEY_PASS_TYPE,
106         SERVICE_KEY_IDENTITY,
107         SERVICE_KEY_PHASE2,
108         SERVICE_KEY_PASSPHRASE,
109         NULL,
110 };
111
112 static void unregister_config(gpointer data)
113 {
114         struct connman_config *config = data;
115
116         connman_info("Removing configuration %s", config->ident);
117
118         g_hash_table_destroy(config->service_table);
119
120         g_free(config->description);
121         g_free(config->name);
122         g_free(config->ident);
123         g_free(config);
124 }
125
126 static void unregister_service(gpointer data)
127 {
128         struct connman_config_service *service = data;
129
130         connman_info("Removing service configuration %s", service->ident);
131
132         protected_services = g_slist_remove(protected_services, service);
133
134         g_free(service->ident);
135         g_free(service->type);
136         g_free(service->name);
137         g_free(service->ssid);
138         g_free(service->eap);
139         g_free(service->identity);
140         g_free(service->ca_cert_file);
141         g_free(service->client_cert_file);
142         g_free(service->private_key_file);
143         g_free(service->private_key_passphrase);
144         g_free(service->private_key_passphrase_type);
145         g_free(service->phase2);
146         g_free(service->passphrase);
147         g_free(service);
148 }
149
150 static void check_keys(GKeyFile *keyfile, const char *group,
151                         const char **possible_keys)
152 {
153         char **avail_keys;
154         gsize nb_avail_keys, i, j;
155
156         avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
157         if (avail_keys == NULL)
158                 return;
159
160         /*
161          * For each key in the configuration file,
162          * verify it is understood by connman
163          */
164         for (i = 0 ; i < nb_avail_keys; i++) {
165                 for (j = 0; possible_keys[j] ; j++)
166                         if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0)
167                                 break;
168
169                 if (possible_keys[j] == NULL)
170                         connman_warn("Unknown configuration key %s in [%s]",
171                                         avail_keys[i], group);
172         }
173
174         g_strfreev(avail_keys);
175 }
176
177 static connman_bool_t
178 is_protected_service(struct connman_config_service *service)
179 {
180         GSList *list;
181
182         DBG("ident %s", service->ident);
183
184         for (list = protected_services; list; list = list->next) {
185                 struct connman_config_service *s = list->data;
186
187                 if (g_strcmp0(s->type, service->type) != 0)
188                         continue;
189
190                 if (s->ssid == NULL || service->ssid == NULL)
191                         continue;
192
193                 if (g_strcmp0(service->type, "wifi") == 0 &&
194                         strncmp(s->ssid, service->ssid, s->ssid_len) == 0) {
195                         return TRUE;
196                 }
197         }
198
199         return FALSE;
200 }
201
202 static int load_service(GKeyFile *keyfile, const char *group,
203                                                 struct connman_config *config)
204 {
205         struct connman_config_service *service;
206         const char *ident;
207         char *str, *hex_ssid;
208         gboolean service_created = FALSE;
209         int err;
210
211         /* Strip off "service_" prefix */
212         ident = group + 8;
213
214         if (strlen(ident) < 1)
215                 return -EINVAL;
216
217         /* Verify that provided keys are good */
218         check_keys(keyfile, group, service_possible_keys);
219
220         service = g_hash_table_lookup(config->service_table, ident);
221         if (service == NULL) {
222                 service = g_try_new0(struct connman_config_service, 1);
223                 if (service == NULL)
224                         return -ENOMEM;
225
226                 service->ident = g_strdup(ident);
227
228                 service_created = TRUE;
229         }
230
231         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_TYPE, NULL);
232         if (str != NULL) {
233                 g_free(service->type);
234                 service->type = str;
235         }
236
237         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_NAME, NULL);
238         if (str != NULL) {
239                 g_free(service->name);
240                 service->name = str;
241         }
242
243         hex_ssid = g_key_file_get_string(keyfile, group, SERVICE_KEY_SSID,
244                                          NULL);
245         if (hex_ssid != NULL) {
246                 char *ssid;
247                 unsigned int i, j = 0, hex;
248                 size_t hex_ssid_len = strlen(hex_ssid);
249
250                 ssid = g_try_malloc0(hex_ssid_len / 2);
251                 if (ssid == NULL) {
252                         err = -ENOMEM;
253                         g_free(hex_ssid);
254                         goto err;
255                 }
256
257                 for (i = 0; i < hex_ssid_len; i += 2) {
258                         sscanf(hex_ssid + i, "%02x", &hex);
259                         ssid[j++] = hex;
260                 }
261
262                 g_free(hex_ssid);
263
264                 g_free(service->ssid);
265                 service->ssid = ssid;
266                 service->ssid_len = hex_ssid_len / 2;
267         } else if (service->name != NULL) {
268                 char *ssid;
269                 unsigned int ssid_len;
270
271                 ssid_len = strlen(service->name);
272                 ssid = g_try_malloc0(ssid_len);
273                 if (ssid == NULL) {
274                         err = -ENOMEM;
275                         goto err;
276                 }
277
278                 memcpy(ssid, service->name, ssid_len);
279                 g_free(service->ssid);
280                 service->ssid = ssid;
281                 service->ssid_len = ssid_len;
282         }
283
284         if (is_protected_service(service) == TRUE) {
285                 connman_error("Trying to provision a protected service");
286                 err = -EACCES;
287                 goto err;
288         }
289
290         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_EAP, NULL);
291         if (str != NULL) {
292                 g_free(service->eap);
293                 service->eap = str;
294         }
295
296         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_CA_CERT, NULL);
297         if (str != NULL) {
298                 g_free(service->ca_cert_file);
299                 service->ca_cert_file = str;
300         }
301
302         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_CL_CERT, NULL);
303         if (str != NULL) {
304                 g_free(service->client_cert_file);
305                 service->client_cert_file = str;
306         }
307
308         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PRV_KEY, NULL);
309         if (str != NULL) {
310                 g_free(service->private_key_file);
311                 service->private_key_file = str;
312         }
313
314         str = g_key_file_get_string(keyfile, group,
315                                                 SERVICE_KEY_PRV_KEY_PASS, NULL);
316         if (str != NULL) {
317                 g_free(service->private_key_passphrase);
318                 service->private_key_passphrase = str;
319         }
320
321         str = g_key_file_get_string(keyfile, group,
322                                         SERVICE_KEY_PRV_KEY_PASS_TYPE, NULL);
323         if (str != NULL) {
324                 g_free(service->private_key_passphrase_type);
325                 service->private_key_passphrase_type = str;
326         }
327
328         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_IDENTITY, NULL);
329         if (str != NULL) {
330                 g_free(service->identity);
331                 service->identity = str;
332         }
333
334         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PHASE2, NULL);
335         if (str != NULL) {
336                 g_free(service->phase2);
337                 service->phase2 = str;
338         }
339
340         str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PASSPHRASE,
341                                         NULL);
342         if (str != NULL) {
343                 g_free(service->passphrase);
344                 service->passphrase = str;
345         }
346
347         if (g_strcmp0(config->ident, NONFS_CONFIG_NAME) != 0)
348                 service->from_fs = TRUE;
349         else
350                 service->from_fs = FALSE;
351
352         if (service_created)
353                 g_hash_table_insert(config->service_table, service->ident,
354                                         service);
355
356         if (config->protected == TRUE)
357                 protected_services =
358                         g_slist_append(protected_services, service);
359
360         connman_info("Adding service configuration %s", service->ident);
361
362         return 0;
363
364 err:
365         if (service_created == TRUE) {
366                 g_free(service->ident);
367                 g_free(service->type);
368                 g_free(service->name);
369                 g_free(service->ssid);
370                 g_free(service);
371         }
372
373         return err;
374 }
375
376 static int load_config(struct connman_config *config)
377 {
378         GKeyFile *keyfile;
379         gsize length;
380         char **groups;
381         char *str;
382         gboolean protected;
383         int i;
384
385         DBG("config %p", config);
386
387         keyfile = __connman_storage_open_config(config->ident);
388         if (keyfile == NULL)
389                 return -EIO;
390
391         /* Verify keys validity of the global section */
392         check_keys(keyfile, "global", config_possible_keys);
393
394         str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL);
395         if (str != NULL) {
396                 g_free(config->name);
397                 config->name = str;
398         }
399
400         str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL);
401         if (str != NULL) {
402                 g_free(config->description);
403                 config->description = str;
404         }
405
406         protected = g_key_file_get_boolean(keyfile, "global",
407                                         CONFIG_KEY_PROT, NULL);
408         config->protected = protected;
409
410         groups = g_key_file_get_groups(keyfile, &length);
411
412         for (i = 0; groups[i] != NULL; i++) {
413                 if (g_str_has_prefix(groups[i], "service_") == TRUE)
414                         load_service(keyfile, groups[i], config);
415         }
416
417         g_strfreev(groups);
418
419         __connman_storage_close_config(config->ident, keyfile, FALSE);
420
421         return 0;
422 }
423
424 static struct connman_config *create_config(const char *ident)
425 {
426         struct connman_config *config;
427
428         DBG("ident %s", ident);
429
430         if (g_hash_table_lookup(config_table, ident) != NULL)
431                 return NULL;
432
433         config = g_try_new0(struct connman_config, 1);
434         if (config == NULL)
435                 return NULL;
436
437         config->ident = g_strdup(ident);
438
439         config->service_table = g_hash_table_new_full(g_str_hash, g_str_equal,
440                                                 NULL, unregister_service);
441
442         g_hash_table_insert(config_table, config->ident, config);
443
444         connman_info("Adding configuration %s", config->ident);
445
446         return config;
447 }
448
449 int __connman_config_load_service(GKeyFile *keyfile, const char *group)
450 {
451         struct connman_config *config = g_hash_table_lookup(config_table,
452                                                         NONFS_CONFIG_NAME);
453
454         if (config == NULL) {
455                 config = create_config(NONFS_CONFIG_NAME);
456                 if (config == NULL)
457                         return -ENOMEM;
458         }
459
460         return load_service(keyfile, group, config);
461 }
462
463 static int read_configs(void)
464 {
465         GDir *dir;
466
467         DBG("");
468
469         dir = g_dir_open(STORAGEDIR, 0, NULL);
470         if (dir != NULL) {
471                 const gchar *file;
472
473                 while ((file = g_dir_read_name(dir)) != NULL) {
474                         GString *str;
475                         gchar *ident;
476
477                         if (g_str_has_suffix(file, ".config") == FALSE)
478                                 continue;
479
480                         ident = g_strrstr(file, ".config");
481                         if (ident == NULL)
482                                 continue;
483
484                         if (g_str_equal(ident, NONFS_CONFIG_NAME) == TRUE)
485                                 continue;
486
487                         str = g_string_new_len(file, ident - file);
488                         if (str == NULL)
489                                 continue;
490
491                         ident = g_string_free(str, FALSE);
492
493                         if (connman_dbus_validate_ident(ident) == TRUE) {
494                                 struct connman_config *config;
495
496                                 config = create_config(ident);
497                                 if (config != NULL)
498                                         load_config(config);
499                         }
500                         g_free(ident);
501                 }
502
503                 g_dir_close(dir);
504         }
505
506         return 0;
507 }
508
509 static gboolean inotify_data(GIOChannel *channel, GIOCondition cond,
510                                                         gpointer user_data)
511 {
512         char buffer[256];
513         char *next_event;
514         gsize bytes_read;
515         GIOStatus status;
516
517         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
518                 inotify_watch = 0;
519                 return FALSE;
520         }
521
522         status = g_io_channel_read_chars(channel, buffer,
523                                         sizeof(buffer) -1, &bytes_read, NULL);
524
525         switch (status) {
526         case G_IO_STATUS_NORMAL:
527                 break;
528         case G_IO_STATUS_AGAIN:
529                 return TRUE;
530         default:
531                 connman_error("Reading from inotify channel failed");
532                 inotify_watch = 0;
533                 return FALSE;
534         }
535
536         next_event = buffer;
537
538         while (bytes_read > 0) {
539                 struct inotify_event *event;
540                 gchar *ext;
541                 gchar *ident;
542                 gsize len;
543
544                 event = (struct inotify_event *) next_event;
545                 if (event->len)
546                         ident = next_event + sizeof(struct inotify_event);
547                 else
548                         ident = NULL;
549
550                 len = sizeof(struct inotify_event) + event->len;
551
552                 /* check if inotify_event block fit */
553                 if (len > bytes_read)
554                         break;
555
556                 next_event += len;
557                 bytes_read -= len;
558
559                 if (ident == NULL)
560                         continue;
561
562                 if (g_str_has_suffix(ident, ".config") == FALSE)
563                         continue;
564
565                 ext = g_strrstr(ident, ".config");
566                 if (ext == NULL)
567                         continue;
568
569                 *ext = '\0';
570
571                 if (g_str_equal(ident, NONFS_CONFIG_NAME) == TRUE)
572                         continue;
573
574                 if (connman_dbus_validate_ident(ident) == FALSE)
575                         continue;
576
577                 if (event->mask & IN_CREATE)
578                         create_config(ident);
579
580                 if (event->mask & IN_MODIFY) {
581                         struct connman_config *config;
582
583                         config = g_hash_table_lookup(config_table, ident);
584                         if (config != NULL) {
585                                 g_hash_table_remove_all(config->service_table);
586                                 load_config(config);
587                         }
588                 }
589
590                 if (event->mask & IN_DELETE)
591                         g_hash_table_remove(config_table, ident);
592         }
593
594         return TRUE;
595 }
596
597 static int create_watch(void)
598 {
599         int fd;
600
601         fd = inotify_init();
602         if (fd < 0)
603                 return -EIO;
604
605         inotify_wd = inotify_add_watch(fd, STORAGEDIR,
606                                         IN_MODIFY | IN_CREATE | IN_DELETE);
607         if (inotify_wd < 0) {
608                 connman_error("Creation of STORAGEDIR  watch failed");
609                 close(fd);
610                 return -EIO;
611         }
612
613         inotify_channel = g_io_channel_unix_new(fd);
614         if (inotify_channel == NULL) {
615                 connman_error("Creation of inotify channel failed");
616                 inotify_rm_watch(fd, inotify_wd);
617                 inotify_wd = 0;
618
619                 close(fd);
620                 return -EIO;
621         }
622
623         g_io_channel_set_close_on_unref(inotify_channel, TRUE);
624         g_io_channel_set_encoding(inotify_channel, NULL, NULL);
625         g_io_channel_set_buffered(inotify_channel, FALSE);
626
627         inotify_watch = g_io_add_watch(inotify_channel,
628                                 G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
629                                 inotify_data, NULL);
630
631         return 0;
632 }
633
634 static void remove_watch(void)
635 {
636         int fd;
637
638         if (inotify_channel == NULL)
639                 return;
640
641         if (inotify_watch > 0) {
642                 g_source_remove(inotify_watch);
643                 inotify_watch = 0;
644         }
645
646         fd = g_io_channel_unix_get_fd(inotify_channel);
647
648         if (inotify_wd >= 0) {
649                 inotify_rm_watch(fd, inotify_wd);
650                 inotify_wd = 0;
651         }
652
653         g_io_channel_unref(inotify_channel);
654 }
655
656 int __connman_config_init(void)
657 {
658         DBG("");
659
660         config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
661                                                 NULL, unregister_config);
662
663         create_watch();
664
665         return read_configs();
666 }
667
668 void __connman_config_cleanup(void)
669 {
670         DBG("");
671
672         remove_watch();
673
674         g_hash_table_destroy(config_table);
675         config_table = NULL;
676 }
677
678 static char *config_pem_fsid(const char *pem_file)
679 {
680         struct statfs buf;
681         unsigned *fsid = (unsigned *) &buf.f_fsid;
682         unsigned long long fsid64;
683
684         if (pem_file == NULL)
685                 return NULL;
686
687         if (statfs(pem_file, &buf) < 0) {
688                 connman_error("statfs error %s for %s",
689                                                 strerror(errno), pem_file);
690                 return NULL;
691         }
692
693         fsid64 = ((unsigned long long) fsid[0] << 32) | fsid[1];
694
695         return g_strdup_printf("%llx", fsid64);
696 }
697
698 static void provision_service(gpointer key, gpointer value, gpointer user_data)
699 {
700         struct connman_service *service = user_data;
701         struct connman_config_service *config = value;
702         struct connman_network *network;
703         const void *ssid;
704         unsigned int ssid_len;
705
706         /* For now only WiFi service entries are supported */
707         if (g_strcmp0(config->type, "wifi") != 0)
708                 return;
709
710         network = __connman_service_get_network(service);
711         if (network == NULL) {
712                 connman_error("Service has no network set");
713                 return;
714         }
715
716         ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len);
717         if (ssid == NULL) {
718                 connman_error("Network SSID not set");
719                 return;
720         }
721
722         if (config->ssid == NULL || ssid_len != config->ssid_len)
723                 return;
724
725         if (memcmp(config->ssid, ssid, ssid_len) != 0)
726                 return;
727
728         /* do not provision immutable services with non-fs originated configs */
729         if (config->from_fs == FALSE &&
730                         __connman_service_get_immutable(service) == TRUE)
731                 return;
732
733         /* only lock services with a config originated from the filesystem */
734         if (config->from_fs == TRUE)
735                 __connman_service_set_immutable(service, TRUE);
736
737         __connman_service_set_favorite(service, TRUE);
738
739         if (config->eap != NULL)
740                 __connman_service_set_string(service, "EAP", config->eap);
741
742         if (config->identity != NULL)
743                 __connman_service_set_string(service, "Identity",
744                                                         config->identity);
745
746         if (config->ca_cert_file != NULL)
747                 __connman_service_set_string(service, "CACertFile",
748                                                         config->ca_cert_file);
749
750         if (config->client_cert_file != NULL)
751                 __connman_service_set_string(service, "ClientCertFile",
752                                                 config->client_cert_file);
753
754         if (config->private_key_file != NULL)
755                 __connman_service_set_string(service, "PrivateKeyFile",
756                                                 config->private_key_file);
757
758         if (g_strcmp0(config->private_key_passphrase_type, "fsid") == 0 &&
759                                         config->private_key_file != NULL) {
760                 char *fsid;
761
762                 fsid = config_pem_fsid(config->private_key_file);
763                 if (fsid == NULL)
764                         return;
765
766                 g_free(config->private_key_passphrase);
767                 config->private_key_passphrase = fsid;
768         }
769
770         if (config->private_key_passphrase != NULL) {
771                 __connman_service_set_string(service, "PrivateKeyPassphrase",
772                                                 config->private_key_passphrase);
773                 /*
774                  * TODO: Support for PEAP with both identity and key passwd.
775                  * In that case, we should check if both of them are found
776                  * from the config file. If not, we should not set the
777                  * service passphrase in order for the UI to request for an
778                  * additional passphrase.
779                  */
780         }
781
782         if (config->phase2 != NULL)
783                 __connman_service_set_string(service, "Phase2", config->phase2);
784
785         if (config->passphrase != NULL)
786                 __connman_service_set_string(service, "Passphrase", config->passphrase);
787 }
788
789 int __connman_config_provision_service(struct connman_service *service)
790 {
791         enum connman_service_type type;
792         GHashTableIter iter;
793         gpointer value, key;
794
795         DBG("service %p", service);
796
797         /* For now only WiFi services are supported */
798         type = connman_service_get_type(service);
799         if (type != CONNMAN_SERVICE_TYPE_WIFI)
800                 return -ENOSYS;
801
802         g_hash_table_iter_init(&iter, config_table);
803
804         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
805                 struct connman_config *config = value;
806
807                 g_hash_table_foreach(config->service_table,
808                                                 provision_service, service);
809         }
810
811         return 0;
812 }