330ae8175da6639770745d63e36160cb796bc132
[platform/upstream/connman.git] / src / config.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/vfs.h>
32 #include <sys/inotify.h>
33 #include <netdb.h>
34 #include <glib.h>
35
36 #include <connman/provision.h>
37 #include <connman/ipaddress.h>
38 #include "connman.h"
39
40 struct connman_config_service {
41         char *ident;
42         char *name;
43         char *type;
44         void *ssid;
45         unsigned int ssid_len;
46         char *eap;
47         char *identity;
48         char *ca_cert_file;
49         char *client_cert_file;
50         char *private_key_file;
51         char *private_key_passphrase;
52         char *private_key_passphrase_type;
53         char *phase2;
54         char *passphrase;
55         GSList *service_identifiers;
56         char *config_ident; /* file prefix */
57         char *config_entry; /* entry name */
58         bool hidden;
59         bool virtual;
60         char *virtual_file;
61         char *ipv4_address;
62         char *ipv4_netmask;
63         char *ipv4_gateway;
64         char *ipv6_address;
65         unsigned char ipv6_prefix_length;
66         char *ipv6_gateway;
67         char *ipv6_privacy;
68         char *mac;
69         char **nameservers;
70         char **search_domains;
71         char **timeservers;
72         char *domain_name;
73 };
74
75 struct connman_config {
76         char *ident;
77         char *name;
78         char *description;
79         GHashTable *service_table;
80 };
81
82 static GHashTable *config_table = NULL;
83
84 static bool cleanup = false;
85
86 /* Definition of possible strings in the .config files */
87 #define CONFIG_KEY_NAME                "Name"
88 #define CONFIG_KEY_DESC                "Description"
89
90 #define SERVICE_KEY_TYPE               "Type"
91 #define SERVICE_KEY_NAME               "Name"
92 #define SERVICE_KEY_SSID               "SSID"
93 #define SERVICE_KEY_EAP                "EAP"
94 #define SERVICE_KEY_CA_CERT            "CACertFile"
95 #define SERVICE_KEY_CL_CERT            "ClientCertFile"
96 #define SERVICE_KEY_PRV_KEY            "PrivateKeyFile"
97 #define SERVICE_KEY_PRV_KEY_PASS       "PrivateKeyPassphrase"
98 #define SERVICE_KEY_PRV_KEY_PASS_TYPE  "PrivateKeyPassphraseType"
99 #define SERVICE_KEY_IDENTITY           "Identity"
100 #define SERVICE_KEY_PHASE2             "Phase2"
101 #define SERVICE_KEY_PASSPHRASE         "Passphrase"
102 #define SERVICE_KEY_HIDDEN             "Hidden"
103
104 #define SERVICE_KEY_IPv4               "IPv4"
105 #define SERVICE_KEY_IPv6               "IPv6"
106 #define SERVICE_KEY_IPv6_PRIVACY       "IPv6.Privacy"
107 #define SERVICE_KEY_MAC                "MAC"
108 #define SERVICE_KEY_NAMESERVERS        "Nameservers"
109 #define SERVICE_KEY_SEARCH_DOMAINS     "SearchDomains"
110 #define SERVICE_KEY_TIMESERVERS        "Timeservers"
111 #define SERVICE_KEY_DOMAIN             "Domain"
112
113 static const char *config_possible_keys[] = {
114         CONFIG_KEY_NAME,
115         CONFIG_KEY_DESC,
116         NULL,
117 };
118
119 static const char *service_possible_keys[] = {
120         SERVICE_KEY_TYPE,
121         SERVICE_KEY_NAME,
122         SERVICE_KEY_SSID,
123         SERVICE_KEY_EAP,
124         SERVICE_KEY_CA_CERT,
125         SERVICE_KEY_CL_CERT,
126         SERVICE_KEY_PRV_KEY,
127         SERVICE_KEY_PRV_KEY_PASS,
128         SERVICE_KEY_PRV_KEY_PASS_TYPE,
129         SERVICE_KEY_IDENTITY,
130         SERVICE_KEY_PHASE2,
131         SERVICE_KEY_PASSPHRASE,
132         SERVICE_KEY_HIDDEN,
133         SERVICE_KEY_IPv4,
134         SERVICE_KEY_IPv6,
135         SERVICE_KEY_IPv6_PRIVACY,
136         SERVICE_KEY_MAC,
137         SERVICE_KEY_NAMESERVERS,
138         SERVICE_KEY_SEARCH_DOMAINS,
139         SERVICE_KEY_TIMESERVERS,
140         SERVICE_KEY_DOMAIN,
141         NULL,
142 };
143
144 static void unregister_config(gpointer data)
145 {
146         struct connman_config *config = data;
147
148         connman_info("Removing configuration %s", config->ident);
149
150         g_hash_table_destroy(config->service_table);
151
152         g_free(config->description);
153         g_free(config->name);
154         g_free(config->ident);
155         g_free(config);
156 }
157
158 static void unregister_service(gpointer data)
159 {
160         struct connman_config_service *config_service = data;
161         struct connman_service *service;
162         char *service_id;
163         GSList *list;
164
165         if (cleanup)
166                 goto free_only;
167
168         connman_info("Removing service configuration %s",
169                                                 config_service->ident);
170
171         if (config_service->virtual)
172                 goto free_only;
173
174         for (list = config_service->service_identifiers; list;
175                                                         list = list->next) {
176                 service_id = list->data;
177
178                 service = __connman_service_lookup_from_ident(service_id);
179                 if (service) {
180                         __connman_service_set_immutable(service, false);
181                         __connman_service_set_config(service, NULL, NULL);
182                         __connman_service_remove(service);
183
184                         /*
185                          * Ethernet or gadget service cannot be removed by
186                          * __connman_service_remove() so reset the ipconfig
187                          * here.
188                          */
189                         if (connman_service_get_type(service) ==
190                                                 CONNMAN_SERVICE_TYPE_ETHERNET ||
191                                         connman_service_get_type(service) ==
192                                                 CONNMAN_SERVICE_TYPE_GADGET) {
193                                 __connman_service_disconnect(service);
194                                 __connman_service_reset_ipconfig(service,
195                                         CONNMAN_IPCONFIG_TYPE_IPV4, NULL, NULL);
196                                 __connman_service_reset_ipconfig(service,
197                                         CONNMAN_IPCONFIG_TYPE_IPV6, NULL, NULL);
198                                 __connman_service_set_ignore(service, true);
199
200                                 /*
201                                  * After these operations, user needs to
202                                  * reconnect ethernet cable to get IP
203                                  * address.
204                                  */
205                         }
206                 }
207
208                 if (!__connman_storage_remove_service(service_id))
209                         DBG("Could not remove all files for service %s",
210                                                                 service_id);
211         }
212
213 free_only:
214         g_free(config_service->ident);
215         g_free(config_service->type);
216         g_free(config_service->name);
217         g_free(config_service->ssid);
218         g_free(config_service->eap);
219         g_free(config_service->identity);
220         g_free(config_service->ca_cert_file);
221         g_free(config_service->client_cert_file);
222         g_free(config_service->private_key_file);
223         g_free(config_service->private_key_passphrase);
224         g_free(config_service->private_key_passphrase_type);
225         g_free(config_service->phase2);
226         g_free(config_service->passphrase);
227         g_free(config_service->ipv4_address);
228         g_free(config_service->ipv4_gateway);
229         g_free(config_service->ipv4_netmask);
230         g_free(config_service->ipv6_address);
231         g_free(config_service->ipv6_gateway);
232         g_free(config_service->ipv6_privacy);
233         g_free(config_service->mac);
234         g_strfreev(config_service->nameservers);
235         g_strfreev(config_service->search_domains);
236         g_strfreev(config_service->timeservers);
237         g_free(config_service->domain_name);
238         g_slist_free_full(config_service->service_identifiers, g_free);
239         g_free(config_service->config_ident);
240         g_free(config_service->config_entry);
241         g_free(config_service->virtual_file);
242         g_free(config_service);
243 }
244
245 static void check_keys(GKeyFile *keyfile, const char *group,
246                         const char **possible_keys)
247 {
248         char **avail_keys;
249         gsize nb_avail_keys, i, j;
250
251         avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
252         if (!avail_keys)
253                 return;
254
255         /*
256          * For each key in the configuration file,
257          * verify it is understood by connman
258          */
259         for (i = 0 ; i < nb_avail_keys; i++) {
260                 for (j = 0; possible_keys[j] ; j++)
261                         if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0)
262                                 break;
263
264                 if (!possible_keys[j])
265                         connman_warn("Unknown configuration key %s in [%s]",
266                                         avail_keys[i], group);
267         }
268
269         g_strfreev(avail_keys);
270 }
271
272 static int check_family(const char *address, int expected_family)
273 {
274         int family;
275         int err = 0;
276
277         family = connman_inet_check_ipaddress(address);
278         if (family < 0) {
279                 DBG("Cannot get address family of %s (%d/%s)", address,
280                         family, gai_strerror(family));
281                 err = -EINVAL;
282                 goto out;
283         }
284
285         switch (family) {
286         case AF_INET:
287                 if (expected_family != AF_INET) {
288                         DBG("Wrong type address %s, expecting IPv4", address);
289                         err = -EINVAL;
290                         goto out;
291                 }
292                 break;
293         case AF_INET6:
294                 if (expected_family != AF_INET6) {
295                         DBG("Wrong type address %s, expecting IPv6", address);
296                         err = -EINVAL;
297                         goto out;
298                 }
299                 break;
300         default:
301                 DBG("Unsupported address family %d", family);
302                 err = -EINVAL;
303                 goto out;
304         }
305
306 out:
307         return err;
308 }
309
310 static int parse_address(const char *address_str, int address_family,
311                         char **address, char **netmask, char **gateway)
312 {
313         char *addr_str, *mask_str, *gw_str;
314         int err = 0;
315         char **route;
316
317         route = g_strsplit(address_str, "/", 0);
318         if (!route)
319                 return -EINVAL;
320
321         addr_str = route[0];
322         if (!addr_str || addr_str[0] == '\0') {
323                 err = -EINVAL;
324                 goto out;
325         }
326
327         if ((err = check_family(addr_str, address_family)) < 0)
328                 goto out;
329
330         mask_str = route[1];
331         if (!mask_str || mask_str[0] == '\0') {
332                 err = -EINVAL;
333                 goto out;
334         }
335
336         gw_str = route[2];
337         if (gw_str && gw_str[0]) {
338                 if ((err = check_family(gw_str, address_family)) < 0)
339                         goto out;
340         }
341
342         g_free(*address);
343         *address = g_strdup(addr_str);
344
345         g_free(*netmask);
346         *netmask = g_strdup(mask_str);
347
348         g_free(*gateway);
349         *gateway = g_strdup(gw_str);
350
351         if (*gateway)
352                 DBG("address %s/%s via %s", *address, *netmask, *gateway);
353         else
354                 DBG("address %s/%s", *address, *netmask);
355
356 out:
357         g_strfreev(route);
358
359         return err;
360 }
361
362 static bool check_address(char *address_str, char **address)
363 {
364         if (g_ascii_strcasecmp(address_str, "auto") == 0 ||
365                         g_ascii_strcasecmp(address_str, "dhcp") == 0 ||
366                         g_ascii_strcasecmp(address_str, "off") == 0) {
367                 *address = address_str;
368                 return false;
369         }
370
371         return true;
372 }
373
374 static bool load_service_generic(GKeyFile *keyfile,
375                         const char *group, struct connman_config *config,
376                         struct connman_config_service *service)
377 {
378         char *str, *mask;
379         char **strlist;
380         gsize length;
381
382         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv4, NULL);
383         if (str && check_address(str, &service->ipv4_address)) {
384                 mask = NULL;
385
386                 if (parse_address(str, AF_INET, &service->ipv4_address,
387                                         &mask, &service->ipv4_gateway) < 0) {
388                         connman_warn("Invalid format for IPv4 address %s",
389                                                                         str);
390                         g_free(str);
391                         goto err;
392                 }
393
394                 if (!g_strrstr(mask, ".")) {
395                         /* We have netmask length */
396                         in_addr_t addr;
397                         struct in_addr netmask_in;
398                         unsigned char prefix_len = 32;
399                         char *ptr;
400                         long int value = strtol(mask, &ptr, 10);
401
402                         if (ptr != mask && *ptr == '\0' && value <= 32)
403                                 prefix_len = value;
404
405                         addr = 0xffffffff << (32 - prefix_len);
406                         netmask_in.s_addr = htonl(addr);
407                         service->ipv4_netmask =
408                                 g_strdup(inet_ntoa(netmask_in));
409
410                         g_free(mask);
411                 } else
412                         service->ipv4_netmask = mask;
413
414                 g_free(str);
415         }
416
417         str =  __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv6, NULL);
418         if (str && check_address(str, &service->ipv6_address)) {
419                 long int value;
420                 char *ptr;
421
422                 mask = NULL;
423
424                 if (parse_address(str, AF_INET6, &service->ipv6_address,
425                                         &mask, &service->ipv6_gateway) < 0) {
426                         connman_warn("Invalid format for IPv6 address %s",
427                                                                         str);
428                         g_free(str);
429                         goto err;
430                 }
431
432                 value = strtol(mask, &ptr, 10);
433                 if (ptr != mask && *ptr == '\0' && value <= 128)
434                         service->ipv6_prefix_length = value;
435                 else
436                         service->ipv6_prefix_length = 128;
437
438                 g_free(mask);
439                 g_free(str);
440         }
441
442         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv6_PRIVACY,
443                                                                         NULL);
444         if (str) {
445                 g_free(service->ipv6_privacy);
446                 service->ipv6_privacy = str;
447         }
448
449         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_MAC, NULL);
450         if (str) {
451                 g_free(service->mac);
452                 service->mac = str;
453         }
454
455         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_DOMAIN, NULL);
456         if (str) {
457                 g_free(service->domain_name);
458                 service->domain_name = str;
459         }
460
461         strlist = __connman_config_get_string_list(keyfile, group,
462                                         SERVICE_KEY_NAMESERVERS,
463                                         &length, NULL);
464         if (strlist) {
465                 if (length != 0) {
466                         g_strfreev(service->nameservers);
467                         service->nameservers = strlist;
468                 } else
469                         g_strfreev(strlist);
470         }
471
472         strlist = __connman_config_get_string_list(keyfile, group,
473                                         SERVICE_KEY_SEARCH_DOMAINS,
474                                         &length, NULL);
475         if (strlist) {
476                 if (length != 0) {
477                         g_strfreev(service->search_domains);
478                         service->search_domains = strlist;
479                 } else
480                         g_strfreev(strlist);
481         }
482
483         strlist = __connman_config_get_string_list(keyfile, group,
484                                         SERVICE_KEY_TIMESERVERS,
485                                         &length, NULL);
486         if (strlist) {
487                 if (length != 0) {
488                         g_strfreev(service->timeservers);
489                         service->timeservers = strlist;
490                 } else
491                         g_strfreev(strlist);
492         }
493
494         return true;
495
496 err:
497         g_free(service->ident);
498         g_free(service->type);
499         g_free(service->ipv4_address);
500         g_free(service->ipv4_netmask);
501         g_free(service->ipv4_gateway);
502         g_free(service->ipv6_address);
503         g_free(service->ipv6_gateway);
504         g_free(service->mac);
505         g_free(service);
506
507         return false;
508 }
509
510 static bool load_service(GKeyFile *keyfile, const char *group,
511                                                 struct connman_config *config)
512 {
513         struct connman_config_service *service;
514         const char *ident;
515         char *str, *hex_ssid;
516         bool service_created = false;
517
518         /* Strip off "service_" prefix */
519         ident = group + 8;
520
521         if (strlen(ident) < 1)
522                 return false;
523
524         /* Verify that provided keys are good */
525         check_keys(keyfile, group, service_possible_keys);
526
527         service = g_hash_table_lookup(config->service_table, ident);
528         if (!service) {
529                 service = g_try_new0(struct connman_config_service, 1);
530                 if (!service)
531                         return false;
532
533                 service->ident = g_strdup(ident);
534
535                 service_created = true;
536         }
537
538         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_TYPE, NULL);
539         if (str) {
540                 g_free(service->type);
541                 service->type = str;
542         } else {
543                 DBG("Type of the configured service is missing for group %s",
544                                                                         group);
545                 goto err;
546         }
547
548         if (!load_service_generic(keyfile, group, config, service))
549                 return false;
550
551         if (g_strcmp0(str, "ethernet") == 0) {
552                 service->config_ident = g_strdup(config->ident);
553                 service->config_entry = g_strdup_printf("service_%s",
554                                                         service->ident);
555
556                 g_hash_table_insert(config->service_table, service->ident,
557                                                                 service);
558                 return true;
559         }
560
561         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_NAME, NULL);
562         if (str) {
563                 g_free(service->name);
564                 service->name = str;
565         }
566
567         hex_ssid = __connman_config_get_string(keyfile, group, SERVICE_KEY_SSID,
568                                          NULL);
569         if (hex_ssid) {
570                 char *ssid;
571                 unsigned int i, j = 0, hex;
572                 size_t hex_ssid_len = strlen(hex_ssid);
573
574                 ssid = g_try_malloc0(hex_ssid_len / 2);
575                 if (!ssid) {
576                         g_free(hex_ssid);
577                         goto err;
578                 }
579
580                 for (i = 0; i < hex_ssid_len; i += 2) {
581                         if (sscanf(hex_ssid + i, "%02x", &hex) <= 0) {
582                                 connman_warn("Invalid SSID %s", hex_ssid);
583                                 g_free(ssid);
584                                 g_free(hex_ssid);
585                                 goto err;
586                         }
587                         ssid[j++] = hex;
588                 }
589
590                 g_free(hex_ssid);
591
592                 g_free(service->ssid);
593                 service->ssid = ssid;
594                 service->ssid_len = hex_ssid_len / 2;
595         } else if (service->name) {
596                 char *ssid;
597                 unsigned int ssid_len;
598
599                 ssid_len = strlen(service->name);
600                 ssid = g_try_malloc0(ssid_len);
601                 if (!ssid)
602                         goto err;
603
604                 memcpy(ssid, service->name, ssid_len);
605                 g_free(service->ssid);
606                 service->ssid = ssid;
607                 service->ssid_len = ssid_len;
608         }
609
610         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_EAP, NULL);
611         if (str) {
612                 g_free(service->eap);
613                 service->eap = str;
614         }
615
616         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_CA_CERT, NULL);
617         if (str) {
618                 g_free(service->ca_cert_file);
619                 service->ca_cert_file = str;
620         }
621
622         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_CL_CERT, NULL);
623         if (str) {
624                 g_free(service->client_cert_file);
625                 service->client_cert_file = str;
626         }
627
628         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PRV_KEY, NULL);
629         if (str) {
630                 g_free(service->private_key_file);
631                 service->private_key_file = str;
632         }
633
634         str = __connman_config_get_string(keyfile, group,
635                                                 SERVICE_KEY_PRV_KEY_PASS, NULL);
636         if (str) {
637                 g_free(service->private_key_passphrase);
638                 service->private_key_passphrase = str;
639         }
640
641         str = __connman_config_get_string(keyfile, group,
642                                         SERVICE_KEY_PRV_KEY_PASS_TYPE, NULL);
643         if (str) {
644                 g_free(service->private_key_passphrase_type);
645                 service->private_key_passphrase_type = str;
646         }
647
648         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IDENTITY, NULL);
649         if (str) {
650                 g_free(service->identity);
651                 service->identity = str;
652         }
653
654         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PHASE2, NULL);
655         if (str) {
656                 g_free(service->phase2);
657                 service->phase2 = str;
658         }
659
660         str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PASSPHRASE,
661                                         NULL);
662         if (str) {
663                 g_free(service->passphrase);
664                 service->passphrase = str;
665         }
666
667         service->config_ident = g_strdup(config->ident);
668         service->config_entry = g_strdup_printf("service_%s", service->ident);
669
670         service->hidden = __connman_config_get_bool(keyfile, group,
671                                                 SERVICE_KEY_HIDDEN, NULL);
672
673         if (service_created)
674                 g_hash_table_insert(config->service_table, service->ident,
675                                         service);
676
677         connman_info("Adding service configuration %s", service->ident);
678
679         return true;
680
681 err:
682         if (service_created) {
683                 g_free(service->ident);
684                 g_free(service->type);
685                 g_free(service->name);
686                 g_free(service->ssid);
687                 g_free(service);
688         }
689
690         return false;
691 }
692
693 static bool load_service_from_keyfile(GKeyFile *keyfile,
694                                                 struct connman_config *config)
695 {
696         bool found = false;
697         char **groups;
698         int i;
699
700         groups = g_key_file_get_groups(keyfile, NULL);
701
702         for (i = 0; groups[i]; i++) {
703                 if (!g_str_has_prefix(groups[i], "service_"))
704                         continue;
705                 if (load_service(keyfile, groups[i], config))
706                         found = true;
707         }
708
709         g_strfreev(groups);
710
711         return found;
712 }
713
714 static int load_config(struct connman_config *config)
715 {
716         GKeyFile *keyfile;
717         char *str;
718
719         DBG("config %p", config);
720
721         keyfile = __connman_storage_load_config(config->ident);
722         if (!keyfile)
723                 return -EIO;
724
725         g_key_file_set_list_separator(keyfile, ',');
726
727         /* Verify keys validity of the global section */
728         check_keys(keyfile, "global", config_possible_keys);
729
730         str = __connman_config_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL);
731         if (str) {
732                 g_free(config->name);
733                 config->name = str;
734         }
735
736         str = __connman_config_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL);
737         if (str) {
738                 g_free(config->description);
739                 config->description = str;
740         }
741
742         if (!load_service_from_keyfile(keyfile, config))
743                 connman_warn("Config file %s/%s.config does not contain any "
744                         "configuration that can be provisioned!",
745                         STORAGEDIR, config->ident);
746
747         g_key_file_free(keyfile);
748
749         return 0;
750 }
751
752 static struct connman_config *create_config(const char *ident)
753 {
754         struct connman_config *config;
755
756         DBG("ident %s", ident);
757
758         if (g_hash_table_lookup(config_table, ident))
759                 return NULL;
760
761         config = g_try_new0(struct connman_config, 1);
762         if (!config)
763                 return NULL;
764
765         config->ident = g_strdup(ident);
766
767         config->service_table = g_hash_table_new_full(g_str_hash, g_str_equal,
768                                                 NULL, unregister_service);
769
770         g_hash_table_insert(config_table, config->ident, config);
771
772         connman_info("Adding configuration %s", config->ident);
773
774         return config;
775 }
776
777 static bool validate_ident(const char *ident)
778 {
779         unsigned int i;
780
781         if (!ident)
782                 return false;
783
784         for (i = 0; i < strlen(ident); i++)
785                 if (!g_ascii_isprint(ident[i]))
786                         return false;
787
788         return true;
789 }
790
791 static int read_configs(void)
792 {
793         GDir *dir;
794
795         DBG("");
796
797         dir = g_dir_open(STORAGEDIR, 0, NULL);
798         if (dir) {
799                 const gchar *file;
800
801                 while ((file = g_dir_read_name(dir))) {
802                         GString *str;
803                         gchar *ident;
804
805                         if (!g_str_has_suffix(file, ".config"))
806                                 continue;
807
808                         ident = g_strrstr(file, ".config");
809                         if (!ident)
810                                 continue;
811
812                         str = g_string_new_len(file, ident - file);
813                         if (!str)
814                                 continue;
815
816                         ident = g_string_free(str, FALSE);
817
818                         if (validate_ident(ident)) {
819                                 struct connman_config *config;
820
821                                 config = create_config(ident);
822                                 if (config)
823                                         load_config(config);
824                         } else {
825                                 connman_error("Invalid config ident %s", ident);
826                         }
827                         g_free(ident);
828                 }
829
830                 g_dir_close(dir);
831         }
832
833         return 0;
834 }
835
836 static void config_notify_handler(struct inotify_event *event,
837                                         const char *ident)
838 {
839         char *ext;
840
841         if (!ident)
842                 return;
843
844         if (!g_str_has_suffix(ident, ".config"))
845                 return;
846
847         ext = g_strrstr(ident, ".config");
848         if (!ext)
849                 return;
850
851         *ext = '\0';
852
853         if (!validate_ident(ident)) {
854                 connman_error("Invalid config ident %s", ident);
855                 return;
856         }
857
858         if (event->mask & IN_CREATE)
859                 create_config(ident);
860
861         if (event->mask & IN_MODIFY) {
862                 struct connman_config *config;
863
864                 config = g_hash_table_lookup(config_table, ident);
865                 if (config) {
866                         int ret;
867
868                         g_hash_table_remove_all(config->service_table);
869                         load_config(config);
870                         ret = __connman_service_provision_changed(ident);
871                         if (ret > 0) {
872                                 /*
873                                  * Re-scan the config file for any
874                                  * changes
875                                  */
876                                 g_hash_table_remove_all(config->service_table);
877                                 load_config(config);
878                                 __connman_service_provision_changed(ident);
879                         }
880                 }
881         }
882
883         if (event->mask & IN_DELETE)
884                 g_hash_table_remove(config_table, ident);
885 }
886
887 int __connman_config_init(void)
888 {
889         DBG("");
890
891         config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
892                                                 NULL, unregister_config);
893
894         connman_inotify_register(STORAGEDIR, config_notify_handler);
895
896         return read_configs();
897 }
898
899 void __connman_config_cleanup(void)
900 {
901         DBG("");
902
903         cleanup = true;
904
905         connman_inotify_unregister(STORAGEDIR, config_notify_handler);
906
907         g_hash_table_destroy(config_table);
908         config_table = NULL;
909
910         cleanup = false;
911 }
912
913 char *__connman_config_get_string(GKeyFile *key_file,
914         const char *group_name, const char *key, GError **error)
915 {
916         char *str = g_key_file_get_string(key_file, group_name, key, error);
917         if (!str)
918                 return NULL;
919
920         return g_strchomp(str);
921 }
922
923 char **__connman_config_get_string_list(GKeyFile *key_file,
924         const char *group_name, const char *key, gsize *length, GError **error)
925 {
926         char **p;
927         char **strlist = g_key_file_get_string_list(key_file, group_name, key,
928                 length, error);
929         if (!strlist)
930                 return NULL;
931
932         p = strlist;
933         while (*p) {
934                 *p = g_strstrip(*p);
935                 p++;
936         }
937
938         return strlist;
939 }
940
941 bool __connman_config_get_bool(GKeyFile *key_file,
942         const char *group_name, const char *key, GError **error)
943 {
944         char *valstr;
945         bool val = false;
946
947         valstr = g_key_file_get_value(key_file, group_name, key, error);
948         if (!valstr)
949                 return false;
950
951         valstr = g_strchomp(valstr);
952         if (strcmp(valstr, "true") == 0 || strcmp(valstr, "1") == 0)
953                 val = true;
954
955         g_free(valstr);
956
957         return val;
958 }
959
960 static char *config_pem_fsid(const char *pem_file)
961 {
962         struct statfs buf;
963         unsigned *fsid = (unsigned *) &buf.f_fsid;
964         unsigned long long fsid64;
965
966         if (!pem_file)
967                 return NULL;
968
969         if (statfs(pem_file, &buf) < 0) {
970                 connman_error("statfs error %s for %s",
971                                                 strerror(errno), pem_file);
972                 return NULL;
973         }
974
975         fsid64 = ((unsigned long long) fsid[0] << 32) | fsid[1];
976
977         return g_strdup_printf("%llx", fsid64);
978 }
979
980 static void provision_service_wifi(struct connman_config_service *config,
981                                 struct connman_service *service,
982                                 struct connman_network *network,
983                                 const void *ssid, unsigned int ssid_len)
984 {
985         if (config->eap)
986                 __connman_service_set_string(service, "EAP", config->eap);
987
988         if (config->identity)
989                 __connman_service_set_string(service, "Identity",
990                                                         config->identity);
991
992         if (config->ca_cert_file)
993                 __connman_service_set_string(service, "CACertFile",
994                                                         config->ca_cert_file);
995
996         if (config->client_cert_file)
997                 __connman_service_set_string(service, "ClientCertFile",
998                                                 config->client_cert_file);
999
1000         if (config->private_key_file)
1001                 __connman_service_set_string(service, "PrivateKeyFile",
1002                                                 config->private_key_file);
1003
1004         if (g_strcmp0(config->private_key_passphrase_type, "fsid") == 0 &&
1005                                         config->private_key_file) {
1006                 char *fsid;
1007
1008                 fsid = config_pem_fsid(config->private_key_file);
1009                 if (!fsid)
1010                         return;
1011
1012                 g_free(config->private_key_passphrase);
1013                 config->private_key_passphrase = fsid;
1014         }
1015
1016         if (config->private_key_passphrase) {
1017                 __connman_service_set_string(service, "PrivateKeyPassphrase",
1018                                                 config->private_key_passphrase);
1019                 /*
1020                  * TODO: Support for PEAP with both identity and key passwd.
1021                  * In that case, we should check if both of them are found
1022                  * from the config file. If not, we should not set the
1023                  * service passphrase in order for the UI to request for an
1024                  * additional passphrase.
1025                  */
1026         }
1027
1028         if (config->phase2)
1029                 __connman_service_set_string(service, "Phase2", config->phase2);
1030
1031         if (config->passphrase)
1032                 __connman_service_set_string(service, "Passphrase",
1033                                                 config->passphrase);
1034
1035         if (config->hidden)
1036                 __connman_service_set_hidden(service);
1037 }
1038
1039 struct connect_virtual {
1040         struct connman_service *service;
1041         const char *vfile;
1042 };
1043
1044 static gboolean remove_virtual_config(gpointer user_data)
1045 {
1046         struct connect_virtual *virtual = user_data;
1047
1048         __connman_service_connect(virtual->service,
1049                                 CONNMAN_SERVICE_CONNECT_REASON_AUTO);
1050         g_hash_table_remove(config_table, virtual->vfile);
1051
1052         g_free(virtual);
1053
1054         return FALSE;
1055 }
1056
1057 static int try_provision_service(struct connman_config_service *config,
1058                                 struct connman_service *service)
1059 {
1060         struct connman_network *network;
1061         const void *service_id;
1062         enum connman_service_type type;
1063         const void *ssid;
1064         unsigned int ssid_len;
1065
1066         type = connman_service_get_type(service);
1067         if (type == CONNMAN_SERVICE_TYPE_WIFI &&
1068                                 g_strcmp0(config->type, "wifi") != 0)
1069                 return -ENOENT;
1070
1071         if (type == CONNMAN_SERVICE_TYPE_ETHERNET &&
1072                                 g_strcmp0(config->type, "ethernet") != 0)
1073                 return -ENOENT;
1074
1075         if (type == CONNMAN_SERVICE_TYPE_GADGET &&
1076                                 g_strcmp0(config->type, "gadget") != 0)
1077                 return -ENOENT;
1078
1079         DBG("service %p ident %s", service,
1080                                         __connman_service_get_ident(service));
1081
1082         network = __connman_service_get_network(service);
1083         if (!network) {
1084                 connman_error("Service has no network set");
1085                 return -EINVAL;
1086         }
1087
1088         DBG("network %p ident %s", network,
1089                                 connman_network_get_identifier(network));
1090
1091         if (config->mac) {
1092                 struct connman_device *device;
1093                 const char *device_addr;
1094
1095                 device = connman_network_get_device(network);
1096                 if (!device) {
1097                         connman_error("Network device is missing");
1098                         return -ENODEV;
1099                 }
1100
1101                 device_addr = connman_device_get_string(device, "Address");
1102
1103                 DBG("wants %s has %s", config->mac, device_addr);
1104
1105                 if (g_ascii_strcasecmp(device_addr, config->mac) != 0)
1106                         return -ENOENT;
1107         }
1108
1109         if (g_strcmp0(config->type, "wifi") == 0 &&
1110                                 type == CONNMAN_SERVICE_TYPE_WIFI) {
1111                 ssid = connman_network_get_blob(network, "WiFi.SSID",
1112                                                 &ssid_len);
1113                 if (!ssid) {
1114                         connman_error("Network SSID not set");
1115                         return -EINVAL;
1116                 }
1117
1118                 if (!config->ssid || ssid_len != config->ssid_len)
1119                         return -ENOENT;
1120
1121                 if (memcmp(config->ssid, ssid, ssid_len) != 0)
1122                         return -ENOENT;
1123         }
1124
1125         if (!config->ipv6_address) {
1126                 connman_network_set_ipv6_method(network,
1127                                                 CONNMAN_IPCONFIG_METHOD_AUTO);
1128         } else if (g_ascii_strcasecmp(config->ipv6_address, "off") == 0) {
1129                 connman_network_set_ipv6_method(network,
1130                                                 CONNMAN_IPCONFIG_METHOD_OFF);
1131         } else if (g_ascii_strcasecmp(config->ipv6_address, "auto") == 0 ||
1132                         g_ascii_strcasecmp(config->ipv6_address, "dhcp") == 0) {
1133                 connman_network_set_ipv6_method(network,
1134                                                 CONNMAN_IPCONFIG_METHOD_AUTO);
1135         } else {
1136                 struct connman_ipaddress *address;
1137
1138                 if (config->ipv6_prefix_length == 0) {
1139                         DBG("IPv6 prefix missing");
1140                         return -EINVAL;
1141                 }
1142
1143                 address = connman_ipaddress_alloc(AF_INET6);
1144                 if (!address)
1145                         return -ENOENT;
1146
1147                 connman_ipaddress_set_ipv6(address, config->ipv6_address,
1148                                         config->ipv6_prefix_length,
1149                                         config->ipv6_gateway);
1150
1151                 connman_network_set_ipv6_method(network,
1152                                                 CONNMAN_IPCONFIG_METHOD_FIXED);
1153
1154                 if (connman_network_set_ipaddress(network, address) < 0)
1155                         DBG("Unable to set IPv6 address to network %p",
1156                                                                 network);
1157
1158                 connman_ipaddress_free(address);
1159         }
1160
1161         if (config->ipv6_privacy) {
1162                 struct connman_ipconfig *ipconfig;
1163
1164                 ipconfig = __connman_service_get_ip6config(service);
1165                 if (ipconfig)
1166                         __connman_ipconfig_ipv6_set_privacy(ipconfig,
1167                                                         config->ipv6_privacy);
1168         }
1169
1170         if (!config->ipv4_address) {
1171                 connman_network_set_ipv4_method(network,
1172                                                 CONNMAN_IPCONFIG_METHOD_DHCP);
1173         } else if (g_ascii_strcasecmp(config->ipv4_address, "off") == 0) {
1174                 connman_network_set_ipv4_method(network,
1175                                                 CONNMAN_IPCONFIG_METHOD_OFF);
1176         } else if (g_ascii_strcasecmp(config->ipv4_address, "auto") == 0 ||
1177                         g_ascii_strcasecmp(config->ipv4_address, "dhcp") == 0) {
1178                 connman_network_set_ipv4_method(network,
1179                                                 CONNMAN_IPCONFIG_METHOD_DHCP);
1180         } else {
1181                 struct connman_ipaddress *address;
1182
1183                 if (!config->ipv4_netmask) {
1184                         DBG("IPv4 netmask missing");
1185                         return -EINVAL;
1186                 }
1187
1188                 address = connman_ipaddress_alloc(AF_INET);
1189                 if (!address)
1190                         return -ENOENT;
1191
1192                 connman_ipaddress_set_ipv4(address, config->ipv4_address,
1193                                         config->ipv4_netmask,
1194                                         config->ipv4_gateway);
1195
1196                 connman_network_set_ipv4_method(network,
1197                                                 CONNMAN_IPCONFIG_METHOD_FIXED);
1198
1199                 if (connman_network_set_ipaddress(network, address) < 0)
1200                         DBG("Unable to set IPv4 address to network %p",
1201                                                                 network);
1202
1203                 connman_ipaddress_free(address);
1204         }
1205
1206         __connman_service_disconnect(service);
1207
1208         service_id = __connman_service_get_ident(service);
1209         config->service_identifiers =
1210                 g_slist_prepend(config->service_identifiers,
1211                                 g_strdup(service_id));
1212
1213         if (!config->virtual)
1214                 __connman_service_set_immutable(service, true);
1215
1216         __connman_service_set_favorite_delayed(service, true, true);
1217
1218         __connman_service_set_config(service, config->config_ident,
1219                                                 config->config_entry);
1220
1221         if (config->domain_name)
1222                 __connman_service_set_domainname(service, config->domain_name);
1223
1224         if (config->nameservers) {
1225                 int i;
1226
1227                 __connman_service_nameserver_clear(service);
1228
1229                 for (i = 0; config->nameservers[i]; i++) {
1230                         __connman_service_nameserver_append(service,
1231                                                 config->nameservers[i], false);
1232                 }
1233         }
1234
1235         if (config->search_domains)
1236                 __connman_service_set_search_domains(service,
1237                                                 config->search_domains);
1238
1239         if (config->timeservers)
1240                 __connman_service_set_timeservers(service,
1241                                                 config->timeservers);
1242
1243         if (g_strcmp0(config->type, "wifi") == 0 &&
1244                                 type == CONNMAN_SERVICE_TYPE_WIFI) {
1245                 provision_service_wifi(config, service, network,
1246                                                         ssid, ssid_len);
1247         } else
1248                 __connman_service_connect(service,
1249                                         CONNMAN_SERVICE_CONNECT_REASON_AUTO);
1250
1251         __connman_service_mark_dirty();
1252
1253         __connman_service_load_modifiable(service);
1254
1255         if (config->virtual) {
1256                 struct connect_virtual *virtual;
1257
1258                 virtual = g_malloc0(sizeof(struct connect_virtual));
1259                 virtual->service = service;
1260                 virtual->vfile = config->virtual_file;
1261
1262                 g_timeout_add(0, remove_virtual_config, virtual);
1263         } else
1264                 __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
1265
1266         return 0;
1267 }
1268
1269 static int find_and_provision_service(struct connman_service *service)
1270 {
1271         GHashTableIter iter, iter_service;
1272         gpointer value, key, value_service, key_service;
1273
1274         g_hash_table_iter_init(&iter, config_table);
1275
1276         while (g_hash_table_iter_next(&iter, &key, &value)) {
1277                 struct connman_config *config = value;
1278
1279                 g_hash_table_iter_init(&iter_service, config->service_table);
1280                 while (g_hash_table_iter_next(&iter_service, &key_service,
1281                                                 &value_service)) {
1282                         if (!try_provision_service(value_service, service))
1283                                 return 0;
1284                 }
1285         }
1286
1287         return -ENOENT;
1288 }
1289
1290 int __connman_config_provision_service(struct connman_service *service)
1291 {
1292         enum connman_service_type type;
1293
1294         /* For now only WiFi, Gadget and Ethernet services are supported */
1295         type = connman_service_get_type(service);
1296
1297         DBG("service %p type %d", service, type);
1298
1299         if (type != CONNMAN_SERVICE_TYPE_WIFI &&
1300                         type != CONNMAN_SERVICE_TYPE_ETHERNET &&
1301                         type != CONNMAN_SERVICE_TYPE_GADGET)
1302                 return -ENOSYS;
1303
1304         return find_and_provision_service(service);
1305 }
1306
1307 int __connman_config_provision_service_ident(struct connman_service *service,
1308                         const char *ident, const char *file, const char *entry)
1309 {
1310         enum connman_service_type type;
1311         struct connman_config *config;
1312         int ret = 0;
1313
1314         /* For now only WiFi, Gadget and Ethernet services are supported */
1315         type = connman_service_get_type(service);
1316
1317         DBG("service %p type %d", service, type);
1318
1319         if (type != CONNMAN_SERVICE_TYPE_WIFI &&
1320                         type != CONNMAN_SERVICE_TYPE_ETHERNET &&
1321                         type != CONNMAN_SERVICE_TYPE_GADGET)
1322                 return -ENOSYS;
1323
1324         config = g_hash_table_lookup(config_table, ident);
1325         if (config) {
1326                 GHashTableIter iter;
1327                 gpointer value, key;
1328                 bool found = false;
1329
1330                 g_hash_table_iter_init(&iter, config->service_table);
1331
1332                 /*
1333                  * Check if we need to remove individual service if it
1334                  * is missing from config file.
1335                  */
1336                 if (file && entry) {
1337                         while (g_hash_table_iter_next(&iter, &key,
1338                                                         &value)) {
1339                                 struct connman_config_service *config_service;
1340
1341                                 config_service = value;
1342
1343                                 if (g_strcmp0(config_service->config_ident,
1344                                                                 file) != 0)
1345                                         continue;
1346
1347                                 if (g_strcmp0(config_service->config_entry,
1348                                                                 entry) != 0)
1349                                         continue;
1350
1351                                 found = true;
1352                                 break;
1353                         }
1354
1355                         DBG("found %d ident %s file %s entry %s", found, ident,
1356                                                                 file, entry);
1357
1358                         if (!found) {
1359                                 /*
1360                                  * The entry+8 will skip "service_" prefix
1361                                  */
1362                                 g_hash_table_remove(config->service_table,
1363                                                 entry + 8);
1364                                 ret = 1;
1365                         }
1366                 }
1367
1368                 find_and_provision_service(service);
1369         }
1370
1371         return ret;
1372 }
1373
1374 static void generate_random_string(char *str, int length)
1375 {
1376         uint8_t val;
1377         int i;
1378
1379         memset(str, '\0', length);
1380
1381         for (i = 0; i < length-1; i++) {
1382                 do {
1383                         val = (uint8_t)(random() % 122);
1384                         if (val < 48)
1385                                 val += 48;
1386                 } while((val > 57 && val < 65) || (val > 90 && val < 97));
1387
1388                 str[i] = val;
1389         }
1390 }
1391
1392 int connman_config_provision_mutable_service(GKeyFile *keyfile)
1393 {
1394         struct connman_config_service *service_config;
1395         struct connman_config *config;
1396         char *vfile, *group;
1397         char rstr[11];
1398
1399         DBG("");
1400
1401         generate_random_string(rstr, 11);
1402
1403         vfile = g_strdup_printf("service_mutable_%s.config", rstr);
1404
1405         config = create_config(vfile);
1406         if (!config)
1407                 return -ENOMEM;
1408
1409         if (!load_service_from_keyfile(keyfile, config))
1410                 goto error;
1411
1412         group = g_key_file_get_start_group(keyfile);
1413
1414         service_config = g_hash_table_lookup(config->service_table, group+8);
1415         if (!service_config)
1416                 goto error;
1417
1418         /* Specific to non file based config: */
1419         g_free(service_config->config_ident);
1420         service_config->config_ident = NULL;
1421         g_free(service_config->config_entry);
1422         service_config->config_entry = NULL;
1423
1424         service_config->virtual = true;
1425         service_config->virtual_file = vfile;
1426
1427         __connman_service_provision_changed(vfile);
1428
1429         if (g_strcmp0(service_config->type, "wifi") == 0)
1430                 __connman_device_request_scan(CONNMAN_SERVICE_TYPE_WIFI);
1431
1432         return 0;
1433
1434 error:
1435         DBG("Could not proceed");
1436         g_hash_table_remove(config_table, vfile);
1437         g_free(vfile);
1438
1439         return -EINVAL;
1440 }
1441
1442 struct connman_config_entry **connman_config_get_entries(const char *type)
1443 {
1444         GHashTableIter iter_file, iter_config;
1445         gpointer value, key;
1446         struct connman_config_entry **entries = NULL;
1447         int i = 0, count;
1448
1449         g_hash_table_iter_init(&iter_file, config_table);
1450         while (g_hash_table_iter_next(&iter_file, &key, &value)) {
1451                 struct connman_config *config_file = value;
1452
1453                 count = g_hash_table_size(config_file->service_table);
1454
1455                 entries = g_try_realloc(entries, (i + count + 1) *
1456                                         sizeof(struct connman_config_entry *));
1457                 if (!entries)
1458                         return NULL;
1459
1460                 g_hash_table_iter_init(&iter_config,
1461                                                 config_file->service_table);
1462                 while (g_hash_table_iter_next(&iter_config, &key,
1463                                                         &value)) {
1464                         struct connman_config_service *config = value;
1465
1466                         if (type &&
1467                                         g_strcmp0(config->type, type) != 0)
1468                                 continue;
1469
1470                         entries[i] = g_try_new0(struct connman_config_entry,
1471                                                 1);
1472                         if (!entries[i])
1473                                 goto cleanup;
1474
1475                         entries[i]->ident = g_strdup(config->ident);
1476                         entries[i]->name = g_strdup(config->name);
1477                         entries[i]->ssid = g_try_malloc0(config->ssid_len + 1);
1478                         if (!entries[i]->ssid)
1479                                 goto cleanup;
1480
1481                         memcpy(entries[i]->ssid, config->ssid,
1482                                                         config->ssid_len);
1483                         entries[i]->ssid_len = config->ssid_len;
1484                         entries[i]->hidden = config->hidden;
1485
1486                         i++;
1487                 }
1488         }
1489
1490         if (entries) {
1491                 entries = g_try_realloc(entries, (i + 1) *
1492                                         sizeof(struct connman_config_entry *));
1493                 if (!entries)
1494                         return NULL;
1495
1496                 entries[i] = NULL;
1497
1498                 DBG("%d provisioned AP found", i);
1499         }
1500
1501         return entries;
1502
1503 cleanup:
1504         connman_config_free_entries(entries);
1505         return NULL;
1506 }
1507
1508 void connman_config_free_entries(struct connman_config_entry **entries)
1509 {
1510         int i;
1511
1512         if (!entries)
1513                 return;
1514
1515         for (i = 0; entries[i]; i++) {
1516                 g_free(entries[i]->ident);
1517                 g_free(entries[i]->name);
1518                 g_free(entries[i]->ssid);
1519                 g_free(entries[i]);
1520         }
1521
1522         g_free(entries);
1523         return;
1524 }
1525
1526 bool __connman_config_address_provisioned(const char *address,
1527                                         const char *netmask)
1528 {
1529         GHashTableIter iter, siter;
1530         gpointer value, key, svalue, skey;
1531
1532         if (!address || !netmask)
1533                 return false;
1534
1535         g_hash_table_iter_init(&iter, config_table);
1536
1537         while (g_hash_table_iter_next(&iter, &key, &value)) {
1538                 struct connman_config *config = value;
1539
1540                 g_hash_table_iter_init(&siter, config->service_table);
1541                 while (g_hash_table_iter_next(&siter, &skey, &svalue)) {
1542                         struct connman_config_service *service = svalue;
1543
1544                         if (!g_strcmp0(address, service->ipv4_address) &&
1545                                         !g_strcmp0(netmask,
1546                                                 service->ipv4_netmask))
1547                                 return true;
1548                 }
1549         }
1550
1551         return false;
1552 }