pptp: Set the username/password before starting daemon
[platform/upstream/connman.git] / vpn / vpn-config.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  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 <unistd.h>
29 #include <string.h>
30 #include <sys/vfs.h>
31 #include <sys/inotify.h>
32 #include <glib.h>
33
34 #include <connman/log.h>
35 #include "../src/connman.h"
36
37 #include "vpn.h"
38
39 enum what {
40         REMOVE = 1,
41         ADD = 2,
42 };
43
44 struct vpn_config_provider {
45         char *provider_identifier;
46         char *ident;
47         char *name;
48         char *type;
49         char *host;
50         char *domain;
51         char *networks;
52         GHashTable *setting_strings;
53
54         char *config_ident; /* file prefix */
55         char *config_entry; /* entry name */
56 };
57
58 struct vpn_config {
59         char *ident;
60         char *name;
61         char *description;
62         connman_bool_t protected;
63         GHashTable *provider_table;
64 };
65
66 static GHashTable *config_table = NULL;
67 static GSList *protected_providers = NULL;
68
69 static connman_bool_t cleanup = FALSE;
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 static const char *config_possible_keys[] = {
77         CONFIG_KEY_NAME,
78         CONFIG_KEY_DESC,
79         CONFIG_KEY_PROT,
80         NULL,
81 };
82
83 static void unregister_config(gpointer data)
84 {
85         struct vpn_config *config = data;
86
87         connman_info("Removing configuration %s", config->ident);
88
89         g_hash_table_destroy(config->provider_table);
90
91         g_free(config->description);
92         g_free(config->name);
93         g_free(config->ident);
94         g_free(config);
95 }
96
97 static void unregister_provider(gpointer data)
98 {
99         struct vpn_config_provider *config_provider = data;
100         struct vpn_provider *provider;
101         char *provider_id;
102
103         if (cleanup == TRUE)
104                 goto free_only;
105
106         provider_id = config_provider->provider_identifier;
107
108         connman_info("Removing provider configuration %s provider %s",
109                                 config_provider->ident, provider_id);
110
111         protected_providers = g_slist_remove(protected_providers,
112                                                 config_provider);
113
114         provider = __vpn_provider_lookup(provider_id);
115         if (provider != NULL)
116                 __vpn_provider_delete(provider);
117         else {
118                 if (__connman_storage_remove_provider(provider_id) == FALSE)
119                         DBG("Could not remove all files for provider %s",
120                                                                 provider_id);
121         }
122
123 free_only:
124         g_free(config_provider->ident);
125         g_free(config_provider->type);
126         g_free(config_provider->name);
127         g_free(config_provider->host);
128         g_free(config_provider->domain);
129         g_free(config_provider->networks);
130         g_hash_table_destroy(config_provider->setting_strings);
131         g_free(config_provider->provider_identifier);
132         g_free(config_provider->config_ident);
133         g_free(config_provider->config_entry);
134         g_free(config_provider);
135 }
136
137 static connman_bool_t check_type(const char *type)
138 {
139         if (g_strcmp0(type, "OpenConnect") == 0)
140                 return TRUE;
141         if (g_strcmp0(type, "OpenVPN") == 0)
142                 return TRUE;
143         if (g_strcmp0(type, "VPNC") == 0)
144                 return TRUE;
145         if (g_strcmp0(type, "L2TP") == 0)
146                 return TRUE;
147         if (g_strcmp0(type, "PPTP") == 0)
148                 return TRUE;
149
150         return FALSE;
151 }
152
153 static connman_bool_t
154 is_protected_provider(struct vpn_config_provider *config_provider)
155 {
156         GSList *list;
157
158         DBG("ident %s", config_provider->ident);
159
160         for (list = protected_providers; list; list = list->next) {
161                 struct vpn_config_provider *p = list->data;
162
163                 if (g_strcmp0(p->type, config_provider->type) != 0)
164                         continue;
165
166                 if (check_type(config_provider->type) == TRUE)
167                         return TRUE;
168         }
169
170         return FALSE;
171 }
172
173 static int set_string(struct vpn_config_provider *config_provider,
174                                         const char *key, const char *value)
175 {
176         DBG("provider %p key %s value %s", config_provider, key, value);
177
178         if (g_str_equal(key, "Type") == TRUE) {
179                 g_free(config_provider->type);
180                 config_provider->type = g_strdup(value);
181         } else if (g_str_equal(key, "Name") == TRUE) {
182                 g_free(config_provider->name);
183                 config_provider->name = g_strdup(value);
184         } else if (g_str_equal(key, "Host") == TRUE) {
185                 g_free(config_provider->host);
186                 config_provider->host = g_strdup(value);
187         } else if (g_str_equal(key, "Domain") == TRUE) {
188                 g_free(config_provider->domain);
189                 config_provider->domain = g_strdup(value);
190         } else if (g_str_equal(key, "Networks") == TRUE) {
191                 g_free(config_provider->networks);
192                 config_provider->networks = g_strdup(value);
193         }
194
195         g_hash_table_replace(config_provider->setting_strings,
196                                         g_strdup(key), g_strdup(value));
197         return 0;
198 }
199
200 static const char *get_string(struct vpn_config_provider *config_provider,
201                                                         const char *key)
202 {
203         DBG("provider %p key %s", config_provider, key);
204
205         if (g_str_equal(key, "Type") == TRUE)
206                 return config_provider->type;
207         else if (g_str_equal(key, "Name") == TRUE)
208                 return config_provider->name;
209         else if (g_str_equal(key, "Host") == TRUE)
210                 return config_provider->host;
211         else if (g_str_equal(key, "Domain") == TRUE)
212                 return config_provider->domain;
213         else if (g_str_equal(key, "Networks") == TRUE)
214                 return config_provider->networks;
215
216         return g_hash_table_lookup(config_provider->setting_strings, key);
217 }
218
219 static void add_keys(struct vpn_config_provider *config_provider,
220                         GKeyFile *keyfile, const char *group)
221 {
222         char **avail_keys;
223         gsize nb_avail_keys, i;
224
225         avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
226         if (avail_keys == NULL)
227                 return;
228
229         for (i = 0 ; i < nb_avail_keys; i++) {
230                 char *value = g_key_file_get_value(keyfile, group,
231                                                 avail_keys[i], NULL);
232                 if (value == NULL) {
233                         connman_warn("Cannot find value for %s",
234                                                         avail_keys[i]);
235                         continue;
236                 }
237
238                 set_string(config_provider, avail_keys[i], value);
239                 g_free(value);
240         }
241
242         g_strfreev(avail_keys);
243 }
244
245 static int load_provider(GKeyFile *keyfile, const char *group,
246                                 struct vpn_config *config, enum what action)
247 {
248         struct vpn_config_provider *config_provider;
249         const char *ident, *host, *domain;
250         int err;
251
252         /* Strip off "provider_" prefix */
253         ident = group + 9;
254
255         if (strlen(ident) < 1)
256                 return -EINVAL;
257
258         config_provider = g_hash_table_lookup(config->provider_table, ident);
259         if (config_provider != NULL)
260                 return -EALREADY;
261
262         config_provider = g_try_new0(struct vpn_config_provider, 1);
263         if (config_provider == NULL)
264                 return -ENOMEM;
265
266         config_provider->ident = g_strdup(ident);
267
268         config_provider->setting_strings = g_hash_table_new_full(g_str_hash,
269                                                 g_str_equal, g_free, g_free);
270
271         add_keys(config_provider, keyfile, group);
272
273         host = get_string(config_provider, "Host");
274         domain = get_string(config_provider, "Domain");
275         if (host != NULL && domain != NULL) {
276                 char *id = __vpn_provider_create_identifier(host, domain);
277
278                 struct vpn_provider *provider;
279                 provider = __vpn_provider_lookup(id);
280                 if (provider != NULL) {
281                         if (action == REMOVE)
282                                 __vpn_provider_delete(provider);
283
284                         g_free(id);
285                         err = -EALREADY;
286                         goto err;
287                 }
288
289                 config_provider->provider_identifier = id;
290
291                 DBG("provider identifier %s", id);
292         } else {
293                 DBG("invalid values host %s domain %s", host, domain);
294                 err = -EINVAL;
295                 goto err;
296         }
297
298         if (is_protected_provider(config_provider) == TRUE) {
299                 connman_error("Trying to provision a protected service");
300                 err = -EACCES;
301                 goto err;
302         }
303
304         config_provider->config_ident = g_strdup(config->ident);
305         config_provider->config_entry = g_strdup_printf("provider_%s",
306                                                 config_provider->ident);
307
308         g_hash_table_insert(config->provider_table,
309                                 config_provider->ident, config_provider);
310
311         if (config->protected == TRUE)
312                 protected_providers =
313                         g_slist_prepend(protected_providers, config_provider);
314
315         err = __vpn_provider_create_from_config(
316                                         config_provider->setting_strings,
317                                         config_provider->config_ident,
318                                         config_provider->config_entry);
319         if (err != 0) {
320                 DBG("Cannot create provider from config file (%d/%s)",
321                         -err, strerror(-err));
322                 goto err;
323         }
324
325         connman_info("Added provider configuration %s",
326                                                 config_provider->ident);
327         return 0;
328
329 err:
330         g_free(config_provider->ident);
331         g_free(config_provider->type);
332         g_free(config_provider->name);
333         g_free(config_provider->host);
334         g_free(config_provider->domain);
335         g_free(config_provider->networks);
336         g_hash_table_destroy(config_provider->setting_strings);
337         g_free(config_provider);
338
339         return err;
340 }
341
342 static void check_keys(GKeyFile *keyfile, const char *group,
343                         const char **possible_keys)
344 {
345         char **avail_keys;
346         gsize nb_avail_keys, i, j;
347
348         avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
349         if (avail_keys == NULL)
350                 return;
351
352         for (i = 0 ; i < nb_avail_keys; i++) {
353                 for (j = 0; possible_keys[j] ; j++)
354                         if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0)
355                                 break;
356
357                 if (possible_keys[j] == NULL)
358                         connman_warn("Unknown configuration key %s in [%s]",
359                                         avail_keys[i], group);
360         }
361
362         g_strfreev(avail_keys);
363 }
364
365 static int load_config(struct vpn_config *config, char *path, enum what action)
366 {
367         GKeyFile *keyfile;
368         GError *error = NULL;
369         gsize length;
370         char **groups;
371         char *str;
372         gboolean protected, found = FALSE;
373         int i;
374
375         DBG("config %p", config);
376
377         keyfile = __connman_storage_load_provider_config(config->ident);
378         if (keyfile == NULL)
379                 return -EIO;
380
381         /* Verify keys validity of the global section */
382         check_keys(keyfile, "global", config_possible_keys);
383
384         str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL);
385         if (str != NULL) {
386                 g_free(config->name);
387                 config->name = str;
388         }
389
390         str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL);
391         if (str != NULL) {
392                 g_free(config->description);
393                 config->description = str;
394         }
395
396         protected = g_key_file_get_boolean(keyfile, "global",
397                                         CONFIG_KEY_PROT, &error);
398         if (error == NULL)
399                 config->protected = protected;
400         else
401                 config->protected = TRUE;
402         g_clear_error(&error);
403
404         groups = g_key_file_get_groups(keyfile, &length);
405
406         for (i = 0; groups[i] != NULL; i++) {
407                 if (g_str_has_prefix(groups[i], "provider_") == TRUE) {
408                         int ret = load_provider(keyfile, groups[i], config,
409                                                 action);
410                         if (ret == 0 || ret == -EALREADY)
411                                 found = TRUE;
412                 }
413         }
414
415         if (found == FALSE)
416                 connman_warn("Config file %s/%s.config does not contain any "
417                         "configuration that can be provisioned!",
418                         path, config->ident);
419
420         g_strfreev(groups);
421
422         g_key_file_free(keyfile);
423
424         return 0;
425 }
426
427 static struct vpn_config *create_config(const char *ident)
428 {
429         struct vpn_config *config;
430
431         DBG("ident %s", ident);
432
433         if (g_hash_table_lookup(config_table, ident) != NULL)
434                 return NULL;
435
436         config = g_try_new0(struct vpn_config, 1);
437         if (config == NULL)
438                 return NULL;
439
440         config->ident = g_strdup(ident);
441
442         config->provider_table = g_hash_table_new_full(g_str_hash, g_str_equal,
443                                                 NULL, unregister_provider);
444
445         g_hash_table_insert(config_table, config->ident, config);
446
447         connman_info("Adding configuration %s", config->ident);
448
449         return config;
450 }
451
452 static connman_bool_t validate_ident(const char *ident)
453 {
454         unsigned int i;
455
456         if (ident == NULL)
457                 return FALSE;
458
459         for (i = 0; i < strlen(ident); i++)
460                 if (g_ascii_isprint(ident[i]) == FALSE)
461                         return FALSE;
462
463         return TRUE;
464 }
465
466 static char *get_dir()
467 {
468         return g_strdup_printf("%s", VPN_STORAGEDIR);
469 }
470
471 static int read_configs(void)
472 {
473         GDir *dir;
474         char *path = get_dir();
475
476         DBG("path %s", path);
477
478         dir = g_dir_open(path, 0, NULL);
479         if (dir != NULL) {
480                 const gchar *file;
481
482                 while ((file = g_dir_read_name(dir)) != NULL) {
483                         GString *str;
484                         gchar *ident;
485
486                         if (g_str_has_suffix(file, ".config") == FALSE)
487                                 continue;
488
489                         ident = g_strrstr(file, ".config");
490                         if (ident == NULL)
491                                 continue;
492
493                         str = g_string_new_len(file, ident - file);
494                         if (str == NULL)
495                                 continue;
496
497                         ident = g_string_free(str, FALSE);
498
499                         if (validate_ident(ident) == TRUE) {
500                                 struct vpn_config *config;
501
502                                 config = create_config(ident);
503                                 if (config != NULL)
504                                         load_config(config, path, ADD);
505                         } else {
506                                 connman_error("Invalid config ident %s", ident);
507                         }
508                         g_free(ident);
509                 }
510
511                 g_dir_close(dir);
512         }
513
514         g_free(path);
515
516         return 0;
517 }
518
519 static void config_notify_handler(struct inotify_event *event,
520                                         const char *ident)
521 {
522         char *ext;
523
524         if (ident == NULL)
525                 return;
526
527         if (g_str_has_suffix(ident, ".config") == FALSE)
528                 return;
529
530         ext = g_strrstr(ident, ".config");
531         if (ext == NULL)
532                 return;
533
534         *ext = '\0';
535
536         if (validate_ident(ident) == FALSE) {
537                 connman_error("Invalid config ident %s", ident);
538                 return;
539         }
540
541         if (event->mask & IN_CREATE)
542                 create_config(ident);
543
544         if (event->mask & IN_MODIFY) {
545                 struct vpn_config *config;
546
547                 config = g_hash_table_lookup(config_table, ident);
548                 if (config != NULL) {
549                         char *path = get_dir();
550
551                         g_hash_table_remove_all(config->provider_table);
552                         load_config(config, path, REMOVE);
553
554                         /* Re-scan the config file for any changes */
555                         g_hash_table_remove_all(config->provider_table);
556                         load_config(config, path, ADD);
557
558                         g_free(path);
559                 }
560         }
561
562         if (event->mask & IN_DELETE)
563                 g_hash_table_remove(config_table, ident);
564 }
565
566 int __vpn_config_init(void)
567 {
568         char *dir = get_dir();
569
570         DBG("");
571
572         config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
573                                                 NULL, unregister_config);
574
575         connman_inotify_register(dir, config_notify_handler);
576
577         g_free(dir);
578
579         return read_configs();
580 }
581
582 void __vpn_config_cleanup(void)
583 {
584         char *dir = get_dir();
585
586         DBG("");
587
588         cleanup = TRUE;
589
590         connman_inotify_unregister(dir, config_notify_handler);
591
592         g_free(dir);
593
594         g_hash_table_destroy(config_table);
595         config_table = NULL;
596
597         cleanup = FALSE;
598 }