vpn: Add null checks
[platform/upstream/connman.git] / plugins / vpn.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 #define _GNU_SOURCE
27 #include <string.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <sys/ioctl.h>
34 #include <sys/types.h>
35 #include <linux/if_tun.h>
36 #include <net/if.h>
37
38 #include <dbus/dbus.h>
39
40 #include <glib/ghash.h>
41 #include <glib/gprintf.h>
42
43 #include <connman/provider.h>
44 #include <connman/log.h>
45 #include <connman/rtnl.h>
46 #include <connman/task.h>
47 #include <connman/inet.h>
48
49 #include "vpn.h"
50
51 struct vpn_data {
52         struct connman_provider *provider;
53         char *if_name;
54         unsigned flags;
55         unsigned int watch;
56         unsigned int state;
57         struct connman_task *task;
58 };
59
60 struct vpn_driver_data {
61         const char *name;
62         const char *program;
63         struct vpn_driver *vpn_driver;
64         struct connman_provider_driver provider_driver;
65 };
66
67 GHashTable *driver_hash = NULL;
68
69 static int stop_vpn(struct connman_provider *provider)
70 {
71         struct vpn_data *data = connman_provider_get_data(provider);
72         struct vpn_driver_data *vpn_driver_data;
73         const char *name;
74         struct ifreq ifr;
75         int fd, err;
76
77         if (data == NULL)
78                 return -EINVAL;
79
80         name = connman_provider_get_driver_name(provider);
81         if (name == NULL)
82                 return -EINVAL;
83
84         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
85
86         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
87                         vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
88                 return 0;
89
90         memset(&ifr, 0, sizeof(ifr));
91         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
92         sprintf(ifr.ifr_name, "%s", data->if_name);
93
94         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
95         if (fd < 0) {
96                 err = -errno;
97                 connman_error("Failed to open /dev/net/tun to device %s: %s",
98                               data->if_name, strerror(errno));
99                 return err;
100         }
101
102         if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
103                 err = -errno;
104                 connman_error("Failed to TUNSETIFF for device %s to it: %s",
105                               data->if_name, strerror(errno));
106                 close(fd);
107                 return err;
108         }
109
110         if (ioctl(fd, TUNSETPERSIST, 0)) {
111                 err = -errno;
112                 connman_error("Failed to set tun device %s nonpersistent: %s",
113                               data->if_name, strerror(errno));
114                 close(fd);
115                 return err;
116         }
117         close(fd);
118         DBG("Killed tun device %s", data->if_name);
119         return 0;
120 }
121
122 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
123 {
124         struct connman_provider *provider = user_data;
125         struct vpn_data *data = connman_provider_get_data(provider);
126         int state = VPN_STATE_FAILURE;
127         enum connman_provider_error ret;
128
129         DBG("provider %p data %p", provider, data);
130
131         if (data == NULL)
132                 goto vpn_exit;
133
134         state = data->state;
135
136         stop_vpn(provider);
137         connman_provider_set_data(provider, NULL);
138         connman_rtnl_remove_watch(data->watch);
139
140 vpn_exit:
141         if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
142                 const char *name;
143                 struct vpn_driver_data *vpn_data = NULL;
144
145                 name = connman_provider_get_driver_name(provider);
146                 if (name != NULL)
147                         vpn_data = g_hash_table_lookup(driver_hash, name);
148
149                 if (vpn_data != NULL &&
150                                 vpn_data->vpn_driver->error_code != NULL)
151                         ret = vpn_data->vpn_driver->error_code(exit_code);
152                 else
153                         ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
154
155                 connman_provider_indicate_error(provider, ret);
156         } else
157                 connman_provider_set_state(provider,
158                                                 CONNMAN_PROVIDER_STATE_IDLE);
159
160         connman_provider_set_index(provider, -1);
161         connman_provider_unref(data->provider);
162
163         g_free(data->if_name);
164         g_free(data);
165
166         connman_task_destroy(task);
167 }
168
169 int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
170 {
171         struct vpn_data *data = connman_provider_get_data(provider);
172         int index;
173
174         if (ifname == NULL || data == NULL)
175                 return  -EIO;
176
177         index = connman_inet_ifindex(ifname);
178         if (index < 0)
179                 return  -EIO;
180
181         if (data->if_name != NULL)
182                 g_free(data->if_name);
183
184         data->if_name = (char *)g_strdup(ifname);
185         connman_provider_set_index(provider, index);
186
187         return 0;
188 }
189
190 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
191 {
192         struct connman_provider *provider = user_data;
193         struct vpn_data *data = connman_provider_get_data(provider);
194
195         if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
196                 if (flags & IFF_UP) {
197                         data->state = VPN_STATE_READY;
198                         connman_provider_set_state(provider,
199                                         CONNMAN_PROVIDER_STATE_READY);
200                 }
201         }
202         data->flags = flags;
203 }
204
205 static void vpn_notify(struct connman_task *task,
206                         DBusMessage *msg, void *user_data)
207 {
208         struct connman_provider *provider = user_data;
209         struct vpn_data *data;
210         struct vpn_driver_data *vpn_driver_data;
211         const char *name;
212         int state, index;
213
214         data = connman_provider_get_data(provider);
215
216         name = connman_provider_get_driver_name(provider);
217         if (name == NULL)
218                 return;
219
220         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
221         if (vpn_driver_data == NULL)
222                 return;
223
224         state = vpn_driver_data->vpn_driver->notify(msg, provider);
225         switch (state) {
226         case VPN_STATE_CONNECT:
227         case VPN_STATE_READY:
228                 index = connman_provider_get_index(provider);
229                 data->watch = connman_rtnl_add_newlink_watch(index,
230                                                      vpn_newlink, provider);
231                 connman_inet_ifup(index);
232                 break;
233
234         case VPN_STATE_UNKNOWN:
235         case VPN_STATE_IDLE:
236         case VPN_STATE_DISCONNECT:
237         case VPN_STATE_FAILURE:
238                 connman_provider_set_state(provider,
239                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
240                 break;
241
242         case VPN_STATE_AUTH_FAILURE:
243                 connman_provider_indicate_error(provider,
244                                         CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
245                 break;
246         }
247 }
248
249 static int vpn_create_tun(struct connman_provider *provider)
250 {
251         struct vpn_data *data = connman_provider_get_data(provider);
252         struct ifreq ifr;
253         int i, fd, index;
254         int ret = 0;
255
256         if (data == NULL)
257                 return -EISCONN;
258
259         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
260         if (fd < 0) {
261                 i = -errno;
262                 connman_error("Failed to open /dev/net/tun: %s",
263                               strerror(errno));
264                 ret = i;
265                 goto exist_err;
266         }
267
268         memset(&ifr, 0, sizeof(ifr));
269         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
270
271         for (i = 0; i < 256; i++) {
272                 sprintf(ifr.ifr_name, "vpn%d", i);
273
274                 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
275                         break;
276         }
277
278         if (i == 256) {
279                 connman_error("Failed to find available tun device");
280                 close(fd);
281                 ret = -ENODEV;
282                 goto exist_err;
283         }
284
285         data->if_name = (char *)g_strdup(ifr.ifr_name);
286         if (data->if_name == NULL) {
287                 connman_error("Failed to allocate memory");
288                 close(fd);
289                 ret = -ENOMEM;
290                 goto exist_err;
291         }
292
293         if (ioctl(fd, TUNSETPERSIST, 1)) {
294                 i = -errno;
295                 connman_error("Failed to set tun persistent: %s",
296                               strerror(errno));
297                 close(fd);
298                 ret = i;
299                 goto exist_err;
300         }
301
302         close(fd);
303
304         index = connman_inet_ifindex(data->if_name);
305         if (index < 0) {
306                 connman_error("Failed to get tun ifindex");
307                 stop_vpn(provider);
308                 ret = -EIO;
309                 goto exist_err;
310         }
311         connman_provider_set_index(provider, index);
312
313         return 0;
314
315 exist_err:
316         return ret;
317 }
318
319 static int vpn_connect(struct connman_provider *provider)
320 {
321         struct vpn_data *data = connman_provider_get_data(provider);
322         struct vpn_driver_data *vpn_driver_data;
323         const char *name;
324         int ret = 0;
325
326         if (data != NULL)
327                 return -EISCONN;
328
329         data = g_try_new0(struct vpn_data, 1);
330         if (data == NULL)
331                 return -ENOMEM;
332
333         data->provider = connman_provider_ref(provider);
334         data->watch = 0;
335         data->flags = 0;
336         data->task = NULL;
337         data->state = VPN_STATE_IDLE;
338
339         connman_provider_set_data(provider, data);
340
341         name = connman_provider_get_driver_name(provider);
342         if (name == NULL)
343                 return -EINVAL;
344
345         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
346
347         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
348                 vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
349
350                 ret = vpn_create_tun(provider);
351                 if (ret < 0)
352                         goto exist_err;
353         }
354
355         data->task = connman_task_create(vpn_driver_data->program);
356
357         if (data->task == NULL) {
358                 ret = -ENOMEM;
359                 stop_vpn(provider);
360                 goto exist_err;
361         }
362
363         if (connman_task_set_notify(data->task, "notify",
364                                         vpn_notify, provider)) {
365                 ret = -ENOMEM;
366                 stop_vpn(provider);
367                 connman_task_destroy(data->task);
368                 data->task = NULL;
369                 goto exist_err;
370         }
371
372         ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
373                                                         data->if_name);
374         if (ret < 0) {
375                 stop_vpn(provider);
376                 connman_task_destroy(data->task);
377                 data->task = NULL;
378                 goto exist_err;
379         }
380
381         DBG("%s started with dev %s",
382                 vpn_driver_data->provider_driver.name, data->if_name);
383
384         data->state = VPN_STATE_CONNECT;
385
386         return -EINPROGRESS;
387
388 exist_err:
389         connman_provider_set_index(provider, -1);
390         connman_provider_set_data(provider, NULL);
391         connman_provider_unref(data->provider);
392         g_free(data->if_name);
393         g_free(data);
394
395         return ret;
396 }
397
398 static int vpn_probe(struct connman_provider *provider)
399 {
400         return 0;
401 }
402
403 static int vpn_disconnect(struct connman_provider *provider)
404 {
405         struct vpn_data *data = connman_provider_get_data(provider);
406         struct vpn_driver_data *vpn_driver_data;
407         const char *name;
408
409         DBG("disconnect provider %p:", provider);
410
411         if (data == NULL)
412                 return 0;
413
414         name = connman_provider_get_driver_name(provider);
415         if (name == NULL)
416                 return 0;
417
418         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
419         if (vpn_driver_data->vpn_driver->disconnect)
420                 vpn_driver_data->vpn_driver->disconnect();
421
422         if (data->watch != 0)
423                 connman_rtnl_remove_watch(data->watch);
424
425         data->watch = 0;
426         data->state = VPN_STATE_DISCONNECT;
427         connman_task_stop(data->task);
428
429         return 0;
430 }
431
432 static int vpn_remove(struct connman_provider *provider)
433 {
434         struct vpn_data *data;
435
436         data = connman_provider_get_data(provider);
437         if (data == NULL)
438                 return 0;
439
440         if (data->watch != 0)
441                 connman_rtnl_remove_watch(data->watch);
442         data->watch = 0;
443         connman_task_stop(data->task);
444
445         g_usleep(G_USEC_PER_SEC);
446         stop_vpn(provider);
447         return 0;
448 }
449
450 static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
451 {
452         struct vpn_driver_data *vpn_driver_data;
453         const char *name;
454
455         name = connman_provider_get_driver_name(provider);
456         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
457         if (vpn_driver_data != NULL &&
458                         vpn_driver_data->vpn_driver->save != NULL)
459                 return vpn_driver_data->vpn_driver->save(provider, keyfile);
460
461         return 0;
462 }
463
464 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
465                         const char *program)
466 {
467         struct vpn_driver_data *data;
468
469         data = g_try_new0(struct vpn_driver_data, 1);
470         if (data == NULL)
471                 return -ENOMEM;
472
473         data->name = name;
474         data->program = program;
475
476         data->vpn_driver = vpn_driver;
477
478         data->provider_driver.name = name;
479         data->provider_driver.disconnect = vpn_disconnect;
480         data->provider_driver.connect = vpn_connect;
481         data->provider_driver.probe = vpn_probe;
482         data->provider_driver.remove = vpn_remove;
483         data->provider_driver.save = vpn_save;
484
485         if (driver_hash == NULL) {
486                 driver_hash = g_hash_table_new_full(g_str_hash,
487                                                         g_str_equal,
488                                                         NULL, g_free);
489         }
490
491         g_hash_table_insert(driver_hash, (char *)name, data);
492
493         connman_provider_driver_register(&data->provider_driver);
494
495         return 0;
496 }
497
498 void vpn_unregister(const char *name)
499 {
500         struct vpn_driver_data *data;
501
502         data = g_hash_table_lookup(driver_hash, name);
503         if (data == NULL)
504                 return;
505
506         connman_provider_driver_unregister(&data->provider_driver);
507
508         g_hash_table_remove(driver_hash, name);
509
510         if (g_hash_table_size(driver_hash) == 0)
511                 g_hash_table_destroy(driver_hash);
512 }