wispr: Possible memory leak in error case
[framework/connectivity/connman.git] / plugins / openconnect.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-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 <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <net/if.h>
31
32 #include <glib.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/provider.h>
37 #include <connman/log.h>
38 #include <connman/task.h>
39 #include <connman/ipconfig.h>
40
41 #include "vpn.h"
42
43 static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
44 {
45         DBusMessageIter iter, dict;
46         const char *reason, *key, *value;
47         const char *domain = NULL;
48         char *addressv4 = NULL, *addressv6 = NULL;
49         char *netmask = NULL, *gateway = NULL;
50         unsigned char prefix_len = 0;
51         struct connman_ipaddress *ipaddress;
52
53         dbus_message_iter_init(msg, &iter);
54
55         dbus_message_iter_get_basic(&iter, &reason);
56         dbus_message_iter_next(&iter);
57
58         if (!provider) {
59                 connman_error("No provider found");
60                 return VPN_STATE_FAILURE;
61         }
62
63         if (strcmp(reason, "connect"))
64                 return VPN_STATE_DISCONNECT;
65
66         domain = connman_provider_get_string(provider, "VPN.Domain");
67
68         dbus_message_iter_recurse(&iter, &dict);
69
70         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
71                 DBusMessageIter entry;
72
73                 dbus_message_iter_recurse(&dict, &entry);
74                 dbus_message_iter_get_basic(&entry, &key);
75                 dbus_message_iter_next(&entry);
76                 dbus_message_iter_get_basic(&entry, &value);
77
78                 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
79                         DBG("%s = %s", key, value);
80
81                 if (!strcmp(key, "VPNGATEWAY"))
82                         gateway = g_strdup(value);
83
84                 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
85                         addressv4 = g_strdup(value);
86
87                 if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
88                         addressv6 = g_strdup(value);
89                         prefix_len = 128;
90                 }
91
92                 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
93                         netmask = g_strdup(value);
94
95                 if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
96                         char *sep;
97
98                         /* The netmask contains the address and the prefix */
99                         sep = strchr(value, '/');
100                         if (sep != NULL) {
101                                 unsigned char ip_len = sep - value;
102
103                                 addressv6 = g_strndup(value, ip_len);
104                                 prefix_len = (unsigned char)
105                                                 strtol(sep + 1, NULL, 10);
106                         }
107                 }
108
109                 if (!strcmp(key, "INTERNAL_IP4_DNS") ||
110                                 !strcmp(key, "INTERNAL_IP6_DNS"))
111                         connman_provider_set_nameservers(provider, value);
112
113                 if (!strcmp(key, "CISCO_PROXY_PAC"))
114                         connman_provider_set_pac(provider, value);
115
116                 if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN"))
117                         domain = value;
118
119                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
120                         g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
121                         connman_provider_append_route(provider, key, value);
122
123                 dbus_message_iter_next(&dict);
124         }
125
126         DBG("%p %p", addressv4, addressv6);
127
128         if (addressv4 != NULL)
129                 ipaddress = connman_ipaddress_alloc(AF_INET);
130         else if (addressv6 != NULL)
131                 ipaddress = connman_ipaddress_alloc(AF_INET6);
132         else
133                 ipaddress = NULL;
134
135         if (ipaddress == NULL) {
136                 g_free(addressv4);
137                 g_free(addressv6);
138                 g_free(netmask);
139                 g_free(gateway);
140
141                 return VPN_STATE_FAILURE;
142         }
143
144         if (addressv4 != NULL)
145                 connman_ipaddress_set_ipv4(ipaddress, addressv4,
146                                                 netmask, gateway);
147         else
148                 connman_ipaddress_set_ipv6(ipaddress, addressv6,
149                                                 prefix_len, gateway);
150         connman_provider_set_ipaddress(provider, ipaddress);
151         connman_provider_set_domain(provider, domain);
152
153         g_free(addressv4);
154         g_free(addressv6);
155         g_free(netmask);
156         g_free(gateway);
157         connman_ipaddress_free(ipaddress);
158
159         return VPN_STATE_CONNECT;
160 }
161
162 static int oc_connect(struct connman_provider *provider,
163                         struct connman_task *task, const char *if_name)
164 {
165         const char *vpnhost, *vpncookie, *cafile, *certsha1, *mtu;
166         int fd, err;
167
168         vpnhost = connman_provider_get_string(provider, "Host");
169         if (!vpnhost) {
170                 connman_error("Host not set; cannot enable VPN");
171                 return -EINVAL;
172         }
173
174         vpncookie = connman_provider_get_string(provider, "OpenConnect.Cookie");
175         if (!vpncookie) {
176                 connman_error("OpenConnect.Cookie not set; cannot enable VPN");
177                 return -EINVAL;
178         }
179
180         certsha1 = connman_provider_get_string(provider,
181                                                 "OpenConnect.ServerCert");
182         if (certsha1)
183                 connman_task_add_argument(task, "--servercert",
184                                                         (char *)certsha1);
185
186         cafile = connman_provider_get_string(provider, "OpenConnect.CACert");
187         mtu = connman_provider_get_string(provider, "VPN.MTU");
188
189         if (cafile)
190                 connman_task_add_argument(task, "--cafile",
191                                                         (char *)cafile);
192         if (mtu)
193                 connman_task_add_argument(task, "--mtu", (char *)mtu);
194
195         connman_task_add_argument(task, "--syslog", NULL);
196         connman_task_add_argument(task, "--cookie-on-stdin", NULL);
197
198         connman_task_add_argument(task, "--script",
199                                   SCRIPTDIR "/openconnect-script");
200
201         connman_task_add_argument(task, "--interface", if_name);
202
203         connman_task_add_argument(task, (char *)vpnhost, NULL);
204
205         err = connman_task_run(task, vpn_died, provider,
206                                &fd, NULL, NULL);
207         if (err < 0) {
208                 connman_error("openconnect failed to start");
209                 return -EIO;
210         }
211
212         if (write(fd, vpncookie, strlen(vpncookie)) !=
213                         (ssize_t)strlen(vpncookie) ||
214                         write(fd, "\n", 1) != 1) {
215                 connman_error("openconnect failed to take cookie on stdin");
216                 return -EIO;
217         }
218
219         return 0;
220 }
221
222 static int oc_save (struct connman_provider *provider, GKeyFile *keyfile)
223 {
224         const char *setting;
225
226         setting = connman_provider_get_string(provider,
227                                         "OpenConnect.ServerCert");
228         if (setting != NULL)
229                 g_key_file_set_string(keyfile,
230                                 connman_provider_get_save_group(provider),
231                                 "OpenConnect.ServerCert", setting);
232
233         setting = connman_provider_get_string(provider,
234                                         "OpenConnect.CACert");
235         if (setting != NULL)
236                 g_key_file_set_string(keyfile,
237                                 connman_provider_get_save_group(provider),
238                                 "OpenConnect.CACert", setting);
239
240         setting = connman_provider_get_string(provider,
241                                         "VPN.MTU");
242         if (setting != NULL)
243                 g_key_file_set_string(keyfile,
244                                 connman_provider_get_save_group(provider),
245                                 "VPN.MTU", setting);
246
247         return 0;
248 }
249
250 static int oc_error_code(int exit_code)
251 {
252
253         switch (exit_code) {
254         case 1:
255                 return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
256         case 2:
257                 return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
258         default:
259                 return CONNMAN_PROVIDER_ERROR_UNKNOWN;
260         }
261 }
262
263 static struct vpn_driver vpn_driver = {
264         .notify         = oc_notify,
265         .connect        = oc_connect,
266         .error_code     = oc_error_code,
267         .save           = oc_save,
268 };
269
270 static int openconnect_init(void)
271 {
272         return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
273 }
274
275 static void openconnect_exit(void)
276 {
277         vpn_unregister("openconnect");
278 }
279
280 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
281         CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)