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